Performance & Scalability пост №2: искусственные лимиты.
От: Maxim S. Shatskih Россия  
Дата: 16.08.07 23:06
Оценка: 15 (3) -1
В Низкоуровнем программировании коллеги предложили накладывать на систему искусственные лимиты. По принципу — если переполняется очередь, то поставь лимит на входе в нее.

А что будут делать те, кто на этот лимит попал? ждать на эвенте? а эвент — это тоже очередь, очередь нитей в ядре.

Итак, из одной очереди сделали две (основная и очередь нитей на эвенте, связанном с лимитом). Какие бонусы мы от этого получили? никаких, кроме аллокации памяти на сами IRPs, которые сидят в основной очереди, и залоканных страниц в MDL буферах, связанных с этими IRPs. Это обычно совсем не заметно, а если лимит наложен в кернеле — то и этого грошового выигрыша нет — в драйвер приходят уже сформированные IRPы с залоканными MDLями.

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

Искусственный лимит — это как охранник на входе в Ашан, который впускает в зал народ порциями. Ну и кому от этого хорошо станет?

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

Итак, искусственные лимиты оправданы (кроме лицензионных соображений) — только нежеланием грузить _машину как целое под завязку_ своим кодом. Например, до такой степени, чтобы не отзывался shell UI.

Теперь — как правильно накладывать эти лимиты. Правильно — это понять, какую именно нагрузку хочется "отмодерить", и накладывать лимит именно туда, на самый низкий уровень. Надо отлимитировать сетевой трафик, создаваемый кодом? поставьте Sleep после send(). Надо отлимитировать дисковый трафик? поставьте Sleep после Read/WriteFile. На какую длительность Sleep? рассчитать от числа пропущенных через IO байт и временных меток начала-конца операции. Элементарно, не буду тут в это вдаваться, напомню только, что точность Sleep — что-то вроде 100ms.

Надо отлимитировать память? создайте объект MemoryBugdet из счетчика, лока и эвента. Эвент открыт, пока счетчик положителен. Пропустите все alloc/free через него. Остальное понятно.

Накладывать лимит на верхнем уровне, например, на реквесты, имеет разумный смысл только в том случае, если вы зовете сложный чужой код, который много всего делает. Можно, например, наложить лимит на SQL запросы или на RPC/DCOM/SOAP транзакции.

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

Идея случайной задержки в случае, когда лимит исчерпан — не менее ужасная. Эта задержка будет заведомо неправильная, если слишком малая — то повтор, если слишком большая, к чему все и придет — то голодание всей системы. Скалабильность — упадет.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: Performance & Scalability пост №2: искусственные лимиты.
От: Cyberax Марс  
Дата: 16.08.07 23:15
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Итак, из одной очереди сделали две (основная и очередь нитей на эвенте, связанном с лимитом). Какие бонусы мы от этого получили? никаких, кроме аллокации памяти на сами IRPs, которые сидят в основной очереди, и залоканных страниц в MDL буферах, связанных с этими IRPs. Это обычно совсем не заметно, а если лимит наложен в кернеле — то и этого грошового выигрыша нет — в драйвер приходят уже сформированные IRPы с залоканными MDLями.

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

MSS>Теперь — как правильно накладывать эти лимиты. Правильно — это понять, какую именно нагрузку хочется "отмодерить", и накладывать лимит именно туда, на самый низкий уровень. Надо отлимитировать сетевой трафик, создаваемый кодом? поставьте Sleep после send(). Надо отлимитировать дисковый трафик? поставьте Sleep после Read/WriteFile. На какую длительность Sleep? рассчитать от числа пропущенных через IO байт и временных меток начала-конца операции. Элементарно, не буду тут в это вдаваться, напомню только, что точность Sleep — что-то вроде 100ms.

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

Такие sleep-хаки могут привести к паталогическим случаям, если две программы будут просыпаться в одно и то же время — они будут обе считать, что процессор загружен на 100%.
Sapienti sat!
Re[2]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 16.08.07 23:45
Оценка: 6 (1)
C>Искусственные лимиты полезны — мы можем дать гарантию, что запрос обработается максимум за N секунд, если он таки был принят на исполнение. Если же у нас нет лимита — возможна ситуация, когда очередь будет расти неограничено.

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

Но это ну крайне сложно, а в не-риалтаймовой ОС — невозможно, хотя бы потому, что вашу нить может прервать еще что-то (UI например, а то и вовсе скрин-сейвер) — и прервать зачастую на значительное время.

Ну или, например, возможные потери пакетов в сети и ретрансмиты. Не думайте, что пакеты теряются только от плохого контакта в проводах — в проводных сетях первая причина потери пакетов — перегрузка раутеров по пути, и именно на эту причину и затюнингован алгоритм slow start/congestion avoidance в TCP.

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

Что же касается попыток дать такие гарантии в, например, системах на базе SQL — то можно, я грустно улыбнусь?

C>За такие хаки я бы лично ногами бил разработчиков. Код должен работать на максимум пропускной способности.


Постойте, а зачем тогда лимиты?

C>Ограничивать ее — дело операционной системы (планировщиков IO,


В какой это ОС у нас есть планировщик дискового IO? в виндах до Висты не было никакого, был только elevator seek и использование SCSI tagged queue, да и в Висте какой-то убогий.

C>процессора, сети — они лучше свою работу знают).


А вы никогда не встречались с требованием "ограничить сетевой трафик, создаваемый системой"? или "ограничить disk IO трафик, создаваемый системой"?

Совершенно запросто под виндами можно написать приложеньице, которое забьет дисковый IO так, что подвиснет shell UI на первом же page fault в shell32.dll или explorer.exe. Юзеры этого не любят.

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

C>Такие sleep-хаки могут привести к паталогическим случаям, если две программы будут просыпаться в одно и то же время — они будут обе считать, что процессор загружен на 100%.


С загрузкой _процессора_ тут надо быть осторожнее, это да, для нее лучше GetThreadTimes использовать — сколько времени бежала именно данная нить на процессоре.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[2]: И еще.
От: Maxim S. Shatskih Россия  
Дата: 16.08.07 23:50
Оценка:
C>Искусственные лимиты полезны — мы можем дать гарантию, что запрос обработается максимум за N секунд, если он таки был принят на исполнение. Если же у нас нет лимита — возможна ситуация, когда очередь будет расти неограничено.

Допустим, вы дали такую гарантию для пути кода C. Но, если отбитые лимитом нити ждут на эвенте, то на совокупность "ожидание лимита+C" уже никакой гарантии не будет.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[3]: Performance & Scalability пост №2: искусственные лими
От: Cyberax Марс  
Дата: 17.08.07 01:06
Оценка: 1 (1)
Здравствуйте, Maxim S. Shatskih, Вы писали:

C>>Искусственные лимиты полезны — мы можем дать гарантию, что запрос обработается максимум за N секунд, если он таки был принят на исполнение. Если же у нас нет лимита — возможна ситуация, когда очередь будет расти неограничено.

MSS>Если у вас просчитаны времена худшего случая для абсолютно всех стадий исполнения, в т.ч., например, время дергания головкой в диске вместе с ее возможной тепловой рекалибрацией, и с учетом других процессов, которые тоже оной головкой дергают (Content Index, например, или да мало ли что там еще в ОС живет)- то да.
MSS>Но это ну крайне сложно, а в не-риалтаймовой ОС — невозможно, хотя бы потому, что вашу нить может прервать еще что-то (UI например, а то и вовсе скрин-сейвер) — и прервать зачастую на значительное время.
Я не говорю про 100%-ную точность до микросекунд с гарантией от попадания астероида. Я говорю про практические гарантии. Например, сетевая карта гарантировано передаст пакет в "ближайшее время", если он все-таки был поставлен в ее очередь — мы можем на это рассчитывать.

Кстати, еще один способ обрабатывать переполнение очереди — просто выбрасывать переполняющие пакеты.

C>>За такие хаки я бы лично ногами бил разработчиков. Код должен работать на максимум пропускной способности.

MSS>Постойте, а зачем тогда лимиты?
Лимиты должны находится в "серверном" коде. Клиент должен либо блокироваться, либо получать EWOULDBLOCK'ом по зубам. Заниматься планированием нагрузки он не должен.

C>>Ограничивать ее — дело операционной системы (планировщиков IO,

MSS>В какой это ОС у нас есть планировщик дискового IO? в виндах до Висты не было никакого, был только elevator seek и использование SCSI tagged queue, да и в Висте какой-то убогий.
Ээээ... Linux (http://www.linuxjournal.com/article/6931 http://kerneltrap.org/node/3851), Solaris, AIX? ionice в Линуксе так уже года четыре есть — использовал пару раз.

C>>процессора, сети — они лучше свою работу знают).

MSS>А вы никогда не встречались с требованием "ограничить сетевой трафик, создаваемый системой"? или "ограничить disk IO трафик, создаваемый системой"?
Не сталкивался. Если бы столкнулся — настроил бы правила файрвола. Disk io-траффик — это сложнее, особенно в Windows.

MSS>Совершенно запросто под виндами можно написать приложеньице, которое забьет дисковый IO так, что подвиснет shell UI на первом же page fault в shell32.dll или explorer.exe. Юзеры этого не любят.

И что дальше? Всем ставить Sleep(100) после каждого вызова ReadFile?

MSS>Вот для реализации этих требований и делают ReadFile+Sleep, что есть полная эмуляция более медленного диска (особенно если нить высокоприоритетна).

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

C>>Такие sleep-хаки могут привести к паталогическим случаям, если две программы будут просыпаться в одно и то же время — они будут обе считать, что процессор загружен на 100%.

MSS>С загрузкой _процессора_ тут надо быть осторожнее, это да, для нее лучше GetThreadTimes использовать — сколько времени бежала именно данная нить на процессоре.
Может быть не процессор, а сеть, например (ее проще загрузить). Умные планировщики знают состояние ВСЕЙ системы и спокойно избегают таких случаев, а вот самопальный хак — самоспалится.
Sapienti sat!
Re: Performance & Scalability пост №2: искусственные лимиты.
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.08.07 09:32
Оценка: 15 (2)
Здравствуйте, Maxim S. Shatskih, Вы писали:

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

MSS>Искусственный лимит — это как охранник на входе в Ашан, который впускает в зал народ порциями. Ну и кому от этого хорошо станет?
MSS>Единственный случай, когда такие лимиты полезны — если хочется, чтобы ваш код не нагружал всю машину в целом на все 100% до трешинга или еще чего. В этом случае лимит подобен охране Макдональдса в 1991 году — если бы не охрана на входе, там была бы опасная для жизни и имущества давка в зале.
MSS>Итак, искусственные лимиты оправданы (кроме лицензионных соображений) — только нежеланием грузить _машину как целое под завязку_ своим кодом. Например, до такой степени, чтобы не отзывался shell UI.

Меня не волнует shell UI, у меня сетевая специфика. А со своей колокольни у меня есть два замечания.

1. Всегда есть момент, когда возрастание внешней нагрузки начинает приводить к нелинейно высокому (квадратичному, экспоненциальному...) росту затрат времени и других внутренних ресурсов. Именно в этот момент часто имеет смысл ввести именно внешнее, "искусственное" ограничение, чтобы сохранить работоспособность системы.

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

2. Во многих случаях аналогом отказа на обслуживание из-за перегрузки является не невпуск в магазин, когда покупатель уже приехал и ему обидно куковать под дверью — а попытка дозвона из дому "когда можно приехать? 14:30? OK" и получением для этого момента логина+пароля на вход. Многие сервисы так работают — особенно автомобильные СТО. Если один магазин перегружен — можно поискать другой, если все легкодоступные перегружены — можно выбрать другое время.

У меня самый близкий пример такой ситуации — пул SMTP серверов. Лучше сделать блокировку новых соединений при перегрузке, чем пытаться это отрабатывать. Если серверов много — отсылающий MTA переберёт несколько по MX'ам, пока не наткнётся на тот, который его примет. Но это лучше, чем если бы он пошёл на перегруженный и целый час ждал ответа на RCPT.

А в случае, например, embedded специфики — подобные искусственные ограничения могут быть и жизненно важными с самого начала. Лучше система обработает, например, 3-х клиентов, но быстро, чем 4-х, но с опозданием хоть на миллисекунду. Разумеется, там должно быть ещё при проектировании системы рассчитано так, чтобы и очереди не создавались.

MSS>Теперь — как правильно накладывать эти лимиты. Правильно — это понять, какую именно нагрузку хочется "отмодерить", и накладывать лимит именно туда, на самый низкий уровень. Надо отлимитировать сетевой трафик, создаваемый кодом? поставьте Sleep после send(). Надо отлимитировать дисковый трафик? поставьте Sleep после Read/WriteFile. На какую длительность Sleep? рассчитать от числа пропущенных через IO байт и временных меток начала-конца операции. Элементарно, не буду тут в это вдаваться, напомню только, что точность Sleep — что-то вроде 100ms.


Следует уточнить, что эти методы годятся только для многонитевой системы и если эти нити не являются дефицитным или слишком дорогим ресурсом. Иначе же эти методы будут только ухудшать. Я уж не вспоминаю, что подобные лимиты обычно имеют смысл только суммарные, и время для того же sleep надо грамотно рассчитать по превышению суммарного лимита (который тоже надо считать грамотно, через шейпинг как минимум на уровне token bucket).

MSS>Но наложение искусственного лимита на самом входе в систему — идея ужасная. В худшем случае юзеры получат идиотское сообщение "попробуйте позже". В лучшем — дикие тормоза.


См. выше. Вы почему-то считаете, что всё, что снаружи — это обязательно "юзеры" со своей спецификой в виде отсутствия терпения и дорогой операцией возврата из состояния "получено "попробуйте позже"". Это так далеко не всегда.
The God is real, unless declared integer.
Re: SEDA
От: remark Россия http://www.1024cores.net/
Дата: 17.08.07 09:52
Оценка: 31 (3)
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Искусственный лимит — это как охранник на входе в Ашан, который впускает в зал народ порциями. Ну и кому от этого хорошо станет?


Не согласен.
При неконтролируемых перегрузках начинается т.н. non-graceful degradation производительности. Производительность может падать практически до нуля и сохраняться на таком уровне некоторое время даже после снятия внешней нагрузки.

Смотри Adaptive Overload Control for Busy Internet Servers
И вообще про архитектуру SEDA

Основная идея такая: вводится синхронный контроль над длинами очередей перед стадиями обработки (ресурсами). Детектируются условия перегрузки системы. Принимаются соотв. меры — со стороны производящей заявки и/или со стороны потребляющей заявки.

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

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


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Sleep
От: remark Россия http://www.1024cores.net/
Дата: 17.08.07 09:55
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>точность Sleep — что-то вроде 100ms.



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


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Sleep
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.08.07 10:26
Оценка: 8 (1)
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Maxim S. Shatskih, Вы писали:


MSS>>точность Sleep — что-то вроде 100ms.


R> :maniac:

R>Я уверен, что Windows способна обеспечивать реактивность для Sleep и других блокирующих операций порядка 10-20 мкс
R>На больше там просто нет работы. А лишние задержки в ОС никто вставлять не будет.
R>Это всё естественно при условии, что есть свободное ядро для выполнения.

Windows — может, и да. А вот архитектура PC/Wintel — ой не всегда. Более-менее подробно я об этом писал тут. Сейчас к этому, конечно, внёс бы поправки. В частности, комбинация, например, ACPI-safe для счёта времени и APIC timer (на тех чипсетах, где он не глючит;)) или HPET для посылки прерываний — могла бы обеспечить такое. Но для этого делать редизайн ОС с возможностью именно раздельного использования двух частей таймера; сомневаюсь, что MS пойдёт на это — по крайней мере в десктопно-серверных версиях. Для realtime — да, ещё как-то реально. Но там нужно далеко не только это перестраивать.
The God is real, unless declared integer.
Re[3]: Sleep
От: remark Россия http://www.1024cores.net/
Дата: 17.08.07 10:48
Оценка:
Здравствуйте, netch80, Вы писали:

N>Windows — может, и да. А вот архитектура PC/Wintel — ой не всегда. Более-менее подробно я об этом писал тут. Сейчас к этому, конечно, внёс бы поправки. В частности, комбинация, например, ACPI-safe для счёта времени и APIC timer (на тех чипсетах, где он не глючит) или HPET для посылки прерываний — могла бы обеспечить такое. Но для этого делать редизайн ОС с возможностью именно раздельного использования двух частей таймера; сомневаюсь, что MS пойдёт на это — по крайней мере в десктопно-серверных версиях. Для realtime — да, ещё как-то реально. Но там нужно далеко не только это перестраивать.


Если "просып" происходит по аппаратному таймеру, то да. Имвхо, "просып" потока может происходить и не по аппаратному таймеру, а после завершения работы другого потока. Тогда ОС будет вызвать шедулер сразу, и тот может сразу сказать, что есть другой поток готовый к выполнению. Хотя тут я не очень силён. Большей частью это догадки и здравый смысл.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Performance & Scalability пост №2: искусственные лимиты.
От: the_dip Таджикистан  
Дата: 17.08.07 12:56
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Напомню только, что точность Sleep — что-то вроде 100ms.


Не совсем так. Есть хитрая функция timeBeginPeriod, которая меняет разрешение планировщика Windows и, соответственно, точность функции Sleep. Прочитать об этом можно, например, здесь: http://www.dtf.ru/articles/read.php?id=39888&page=2
Re[4]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 13:40
Оценка:
C>Кстати, еще один способ обрабатывать переполнение очереди — просто выбрасывать переполняющие пакеты.

В низкоуровневых протоколах, которые либо заявлены как ненадежные (UDP), либо имеют ретрансмит (TCP) — это и делается.

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

C>Лимиты должны находится в "серверном" коде. Клиент должен либо блокироваться, либо получать EWOULDBLOCK'ом по зубам. Заниматься планированием нагрузки он не должен.


Не всегда речь об архитектурах клиент-сервер. Часто речь о настольной апликации, которая на 100% грузит disk IO.

Что до клиент-сервер — то тут да, я не понимаю, зачем лимиты в клиенте.

MSS>>В какой это ОС у нас есть планировщик дискового IO? в виндах до Висты не было никакого, был только elevator seek и использование SCSI tagged queue, да и в Висте какой-то убогий.

C>Ээээ... Linux (http://www.linuxjournal.com/article/6931

И чего? примерно то же, что в виндах, разница в мелочи. Самое главное — в статье по ссылке нет понятия "IO приоритет процесса" и планирования дисковых реквестов в зависимости от того, кто их издал. Этого нет и в виндах.

C>http://kerneltrap.org/node/3851


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

), Solaris, AIX? ionice в Линуксе так уже года четыре есть — использовал пару раз.

А сколько народу у нас пишут под винды? а насколько портабелен такой код?

C>Не сталкивался. Если бы столкнулся — настроил бы правила файрвола.


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

C>Disk io-траффик — это сложнее, особенно в Windows.


Что сложного? добавь Sleep, и все. Альтернатив в винде нет — там lazy writer не помнит, какой процесс испачкал страницы кэша, таким образом, задать по-процессный приоритет _на слив кэша_ невозможно в принципе.

C>И что дальше? Всем ставить Sleep(100) после каждого вызова ReadFile?


Не всем, и не 100. Надо замерять, сколько времени выполнялись дисковые операции, каков незатротленный disk IO bandwidth, и на основании этого рассчитывать длительность каждого Sleep персонально. Иногда она может оказаться равной нулю, в коем случае вообще не надо звать Sleep.

MSS>>Вот для реализации этих требований и делают ReadFile+Sleep, что есть полная эмуляция более медленного диска (особенно если нить высокоприоритетна).

C>Не делает. Это идиотский хак, который маскирует убогость планировщика.

Вот жизнь у нас такая идиотская. Ну нет в винде управляемого планировщика дисков и понятия "IO приоритет процесса", и часть причин к тому — архитектурные (Cc не помнит, какой процесс какие страницы испачкал). Живем мы так.

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

Если параметр Sleep правильно рассчитывается — то можно системе извне задать значение "процент disk IO bandwidth, который позволено откушать". И одним лишь Sleep вместе со взятием временных меток — задача решается с достаточной степенью точности.

C>Может быть не процессор, а сеть, например (ее проще загрузить).


Нет, с процессором у нас чуть иначе все. С процессором сложность в снятии адекватных статов его загрузки данной нитью. Снятие меток времени до и после CPU-bound кода — действительно идиотизм, ты прав. Тут нужен только и исключительно GetThreadTimes.

C>Умные планировщики знают состояние ВСЕЙ системы и спокойно избегают таких случаев, а вот самопальный хак — самоспалится.


Что вы предлагаете? для ограничения сетевого трафика, создаваемого софтом, обращаться к psched? какова цена реализации такого дела?

Как ограничить дисковый трафик конкретного процесса на виндах?

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

При правильном кодировании ничего там не самоспалится, или самоспалится ненамного, не навсегда и в предсказуемую сторону.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: Performance & Scalability пост №2: искусственные лимиты.
От: GlebZ Россия  
Дата: 17.08.07 13:43
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

Возможно при высокой гранулярности операций в низкоуровневом программировании — искуственный лимит и плохо, но если это бизнес-транзакции — то скорее строго наоборот. Линейная зависимость между количеством пользователей и временем ответа, то есть идеальная масштабируемость — это большая редкость. Масштабируемость собственно и измеряется в том, когда эта зависимость из линейной переходит в экспоненту.
Для бизнес-транзакций значительно лучше если из 100 транзакций проходит 90 успешно, а оставшиеся 10 уходят в сад. Без искуственного лимита — в сад уйдут все 100 транзакций из-за конкуренции за ресурсы. И чем более высокоуровневый лимит — тем дешевле решение достаточно непростой задачи.

PS. Если еще задуматься о DDoS атаках, то все еще более усложнится. Нормальный искуственный лимит — вполне решит данную задачу.
Re[2]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 13:56
Оценка:
N>1. Всегда есть момент, когда возрастание внешней нагрузки начинает приводить к нелинейно высокому (квадратичному, экспоненциальному...) росту затрат времени и других внутренних ресурсов. Именно в этот момент часто имеет смысл ввести именно внешнее, "искусственное" ограничение, чтобы сохранить работоспособность системы.

Бывает.

N>2. Во многих случаях аналогом отказа на обслуживание из-за перегрузки является не невпуск в магазин, когда покупатель уже приехал и ему обидно куковать под дверью — а попытка дозвона из дому "когда можно приехать? 14:30? OK" и получением для этого момента логина+пароля на вход. Многие сервисы так работают — особенно автомобильные СТО. Если один магазин перегружен — можно поискать другой, если все легкодоступные перегружены — можно выбрать другое время.


Хорошо, если ваша апликация позволяет такую архитектуру.

N>У меня самый близкий пример такой ситуации — пул SMTP серверов. Лучше сделать блокировку новых соединений при перегрузке, чем пытаться это отрабатывать. Если серверов много — отсылающий MTA переберёт несколько по MX'ам, пока не наткнётся на тот, который его примет. Но это лучше, чем если бы он пошёл на перегруженный и целый час ждал ответа на RCPT.


Совершенно нормально. Но это специфика конкретной апликации, причем высокоуровневой.

N>А в случае, например, embedded специфики — подобные искусственные ограничения могут быть и жизненно важными с самого начала. Лучше система обработает, например, 3-х клиентов, но быстро, чем 4-х, но с опозданием хоть на миллисекунду.


Это называется realtime.

N>Разумеется, там должно быть ещё при проектировании системы рассчитано так, чтобы и очереди не создавались.


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

N>Следует уточнить, что эти методы годятся только для многонитевой системы и если эти нити не являются дефицитным или слишком дорогим ресурсом.


Естественно, если речь о dedicated нити, которая делает synchronous disk IO. Как это сделать с overlapped IO — хорошая задача олимпиадного толка

N>Иначе же эти методы будут только ухудшать. Я уж не вспоминаю, что подобные лимиты обычно имеют смысл только суммарные, и время для того же sleep надо грамотно рассчитать по превышению суммарного лимита


Это очевидно. Т.е. лимитируется на деле disk IO bandwidth, а не отдельные операции. Создается эмуляция более медленного диска.

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


В вашем конкретном случае вашей конкретной системы — да, вы правы.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[2]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 13:59
Оценка:
_>Не совсем так. Есть хитрая функция timeBeginPeriod, которая меняет разрешение планировщика Windows и, соответственно, точность функции Sleep. Прочитать об этом можно, например, здесь: http://www.dtf.ru/articles/read.php?id=39888&page=2

Есть, конечно, функция общеизвестна. Но далеко не всегда есть смысл трогать ОС как целое ради своего приложения.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[2]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 14:27
Оценка:
GZ>Линейная зависимость между количеством пользователей и временем ответа, то есть идеальная масштабируемость — это большая редкость. Масштабируемость собственно и измеряется в том, когда эта зависимость из линейной переходит в экспоненту.

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

Но это идеал.

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

GZ>PS. Если еще задуматься о DDoS атаках, то все еще более усложнится. Нормальный искуственный лимит — вполне решит данную задачу.


Ну тут конечно
Занимайтесь LoveCraftом, а не WarCraftом!
Re[3]: Performance & Scalability пост №2: искусственные лими
От: GlebZ Россия  
Дата: 17.08.07 15:07
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Идеальная масштабируемость — это почти полная независимость времени ответа от количества пользователей при линейном росте показателей нагрузки системы (процессор, диски, сеть) с ростом числа пользователей. Т.е. пока процессор или диски не уперлись в 100% — время ответа не должно зависеть от нагрузки.


MSS>Но это идеал.

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

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

Все значительно сложнее. Поэтому я бы никогда не предлагал делать предварительную оптимизацию, а действовать только по факту с помощью нагрузочного тестирования и профайлера.
Re[4]: Performance & Scalability пост №2: искусственные лими
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 16:08
Оценка:
GZ>Это как раз не идеал. Если при нагрузочном тестировании я получил этот график, то это тревожный звонок о том, что система не может качественно адаптировать ресурсы под свои нужды.

Нет.

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

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

Это практически полное отсутствие скалабилити.

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

Значит, растет другое слагаемое общего времени выполнения. Слагаемое не связано с исполнением IO или длинной работой процессора — общая загрузка процессора даже чуть падает. С сетевым трафиком оно тоже не связано. Значит, остается _ожидание на локах и в очередях_.

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

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

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

Так что такая картина — это очень плохо.

Пример из ядра виндов — долгое время MS сам использовал очередь, реализованную IoStartPacket/IoStartNextPacket, и другим рекомендовал. Так вот, _все эти очереди в системе вообще_ защищены одним глобальным спинлоком — им же защищен вызов IoCancelIrp, поскольку очереди поддерживают отмену реквестов. Положить реквест на любую из них — взять этот лок. Потребить реквест из любой из этих очередей — опять взять этот лок.

Приходилось слышать, что contention на IopCancelLock был чудовищный, и создавал измеримое падение скалабильности ОС.

Теперь эта очередь у нас deprecated, а рекомендуется к использованию очередь IoCsqInsertIrp — она дана в либе для старых ОС (w2k) и встроена в сам кернел, начиная, как я помню, с XP. Можно и свою cancel-safe queue написать, если уверен, что сделаешь правильно — Walter Oney вон написал, и страницы на две в книге доказал правильность.

В этих очередях на каждую очередь свой спинлок.

Ну или взять lock_kernel() в ядре Линукса, от которого они плавно уходят, начиная с 2.4, и который был сделан как poor man's SMP support на коленке, чтобы поддержать SMP, не переписывая при этом изначально не SMPшный код почти всего кернела (в виндах все изначально SMPшное. Тем позорнее наличие IopCancelLock).

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


Нет.

Еще раз — "почти независимость" времени выполнения запроса от количества запросов (до какого-то потолка, на котором это кончается, а начинается трешинг с обвальным ростом времени выполнения), при линейном росте показателей нагрузки машины от количества запросов — вот это правильная скалабильность.

Это означает, что время ожидания на локах почти не вносит вклада во время исполнения, т.е. этот вклад вносится только неизбежными вещами типа "надо исполнить вот этот код", "надо исполнить вот этот запрос" и "надо прочитать с диска вот столько". Т.е. скалабильность хороша, ее оптимизировать не надо (хотя, возможно, надо оптимизировать производительность).

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

Признаки обвала скалабильности — разные. Не обязательно это трешинг на виртуальной памяти. Например, картина, когда на машине 100% CPU load, и показатель Processor Queue Length велик — это показания к апгрейду "камня" или увеличению количества "камней". С дисками примерно та же картина.

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


Ну очевидные вещи надо делать сразу, т.е. просто не делать глупостей изначально.

Что же касается оптимизации — то без цифр ее делать действительно нельзя. Не обязательно иметь профайлер, хватит чего-то вроде printf(GetSystemTimeAsFileTime()) из-под отладочного макроса.

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

А вот анализатор SQL запросов — крайне полезен.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[5]: Performance & Scalability пост №2: искусственные лими
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.08.07 10:57
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

GZ>>Это как раз не идеал. Если при нагрузочном тестировании я получил этот график, то это тревожный звонок о том, что система не может качественно адаптировать ресурсы под свои нужды. ;)

MSS>Нет.
MSS>"Тревожный звонок" — это когда при росте числа посылаемых запросов линейно с ним растет время исполнения каждого запроса, _а показатели нагрузки машины типа процессора или диска такие же, причем низкие, и даже чуть падают_.

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

Вы почему-то концентрируетесь на одном варианте (спячка на общем локе, другой вариант затора на ресурсе с ожиданием его освобождения). Возможно, в Вашей практике иное практически не встречается, поэтому Вы столь прямолинейно и безвариантно говорите "нет" на иные интерпретации. Вот этого я не могу понять. Бывает и такая симптоматика, как Вы описали (падение загрузки из-за блокировок), так и обратная ситуация — нелинейный рост загрузки, как минимум в двух основных вариантах — прямая алгоритмическая неэффективность (например, затраты O(N^2)) и соревнования за общие ресурсы без сна на блокировках (или неадекватная реализация, или просто невозможно оказалось сделать эффективный сон).

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

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

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


То же чрезмерное обобщение.

MSS>Ну или взять lock_kernel() в ядре Линукса, от которого они плавно уходят, начиная с 2.4,


Не с 2.4, а с 2.1. Один глобальный лок был характерен только для 2.0, брался на входе в сисколл и/или прерывание. В 2.1 начали это разносить. В 2.2 уже заметная часть простых операций была распараллелена с использованием местных локов, к 2.4 гранулирование было доведено до разумного предела, но без замены основных механизмов.

MSS> и который был сделан как poor man's SMP support на коленке, чтобы поддержать SMP, не переписывая при этом изначально не SMPшный код почти всего кернела (в виндах все изначально SMPшное. Тем позорнее наличие IopCancelLock).


Может, его отмене мешает ABI compatibility?

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


MSS>Еще раз — "почти независимость" времени выполнения запроса от количества запросов (до какого-то потолка, на котором это кончается, а начинается трешинг с обвальным ростом времени выполнения), при линейном росте показателей нагрузки машины от количества запросов — вот это правильная скалабильность.


Не бывает масштабируемость (AKA скалабильность) "правильная" или "неправильная" сама по себе. Её правильность может быть только сравнительной — и проверяется возможностью реализации, которая на тех же ресурсах будет давать бОльшую производительность. Ограничивая понятие масштабируемости случаем явного недопотребления наличных ресурсов, Вы теряете слишком много и попросту действуете непрактично.
The God is real, unless declared integer.
Re[3]: Performance & Scalability пост №2: искусственные лими
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.08.07 11:14
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>В вашем конкретном случае вашей конкретной системы — да, вы правы.


А в общем случае — неправ? А почему собственно?;)))
В продолжение предыдущего письма: представьте себе: система при одном запросе в секунду потребляет 1% процессора, двух — 4%, трёх — 9%, четырёх — 16%... характеристика нелинейна. Превышения по процессору пока нет. Система масштабируема? По-Вашему — у неё нет проблем масштабируемости. По-моему — есть.

N>>2. Во многих случаях аналогом отказа на обслуживание из-за перегрузки является не невпуск в магазин, когда покупатель уже приехал и ему обидно куковать под дверью — а попытка дозвона из дому "когда можно приехать? 14:30? OK" и получением для этого момента логина+пароля на вход. Многие сервисы так работают — особенно автомобильные СТО. Если один магазин перегружен — можно поискать другой, если все легкодоступные перегружены — можно выбрать другое время.

MSS>Хорошо, если ваша апликация позволяет такую архитектуру.

Да, я знаю, что это "удобный" случай. И что бывают "неудобные".
The God is real, unless declared integer.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.