Сергей Деревяго

Горизонтальное масштабирование



1. Введение

Итак, что же такое Горизонтальное масштабирование?

Смело гуглим горизонтальное масштабирование и смотрим на верхние ссылки:

“Вы не влипнете в недостаток ресурсов“ -- блестящая фраза! Деньги тоже ресурс, и вы обязательно влипнете.

Почему? Они все время "забывают" вам сказать, какой сервер стоит масштабировать... И какой точно нет!

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


2. Учись, студент!

Десять студентов ждали минуту. Сколько всего они ожидали?

Не, ну смешно! Сами ж сказали, что ждали минуту.

Сказали, но есть нюанс...

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

Они тупо стояли и ждали. А могли бы считать ворон.

Нет, погодите! Хоть десять, хоть двадцать студентов, но мои «Водолазные» Челябинского завода четко показывают, что прошла всего лишь минута.

Все правильно, часы часам рознь! Именно в ней и скрывается суть Масштабирования.

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

Смотрите:

И вроде бы это одно и то же... Но!

  1. Горизонтальное масштабирование вам никак не поможет, если результаты нужны раньше минуты! Студенту необходима минута, как девять месяцев женщине Брукса.
  2. Вертикальное масштабирование выручит, когда этой минуты нет. Но надо платить за апгрейд!
  3. При наличии пары минут, горизонтальное и вертикальное масштабирование начинают быть очень похожи по результату. Но, конечно, не по цене.

3. Суть проблемы

Клиент договорился с сервером, процесс пошел:

И все больше и больше желающих:

Так, что сервер уже не может!

Что делать? Расширять производство! Т.е. масштабировать сервер.

Но не очень спешите, пока не увидели:

Ой! Их тут целая банда?!

Да, ребята, в реальной жизни за фронт сервером прячется тазик товарищей! И чтобы не скучно, они ездят на базу:

Вот теперь можно и масштабировать!

Раз Server не справляется с Client-ами, самое время стартануть десяточку дополнительных инстансов...

Гмм, а если не будет толку? Вдруг фронт Server не успевает, потому что его тормозит Server L? Или Server R2??

Ай, чтобы долго не мучаться, мы сейчас стартанем по десяточке ВСЕХ дополнительных.

Шарик, ты балбес!

Ой, так нельзя. Поздравляю, Шарик, ты балбес! Добавить всех по десяточке -- верный способ слить деньги (AWS, Azure, GCP… все они аплодируют стоя).

Так, а что делать?

Все очень просто:

  1. Ищем узкое место и вдумчиво добавляем инстансы. Только туда!
  2. По возможности, ищем широкие места и убираем лишние инстансы. Аккуратно.

4. Слабое звено

Мы все Творцы и вкладываем Душу в свое дело!

Стараемся сделать получше. Мощнее. Изящнее!

Ускорить в несколько раз компонент и доказать слова тестами.

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

Постоянно имейте в виду, что мы масштабируем сервисы ради потребностей бизнеса! Мы не масштабируем, если нет выхлопа. Точка.

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

Смотрите, даже ДЕСЯТИКРАТНОЕ ускорение "какого-то компонента" просто никто не заметит, если раньше там набегало лишние 10 секунд за сутки, а теперь стала только одна. Это практически бесполезно. А время и деньги уже потрачены...

Так что же, нет смысла стараться?!

Ради этого места реального бизнеса АБСОЛЮТНО нет смысла стараться. Хуже дурака -- только дурак с инициативой!

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

Тут самое время напомнить один из лучших фрагментов книги Gene Kim, Kevin Behr, George Spafford "The Phoenix Project":

“Let me tell you a story. Decades ago, there used to be a guy named Mark. He was the supervisor for that first work center, right down there by that desk. Those racks hold the folders for incoming jobs. Isn’t it amazing that those folders look exactly like they did back then?

“At any rate,” he continues, “one day I see Mark picking up a folder to start some job. I ask him, ‘On what basis did you choose that job, versus any of the others?’

“And you know what he tells me? He says, ‘It’s a job that requires this work center first. And we’re open.’”

He shakes his head incredulously. “I could hardly believe it. I tell him, ‘Your station is just the first of twenty operations. You don’t factor the availability of any of the other nineteen stations in your decision?’ And he replies, ‘Well, no. This is the way I’ve done it for twenty years.’”

He laughs. “I suppose to him, it sounds like a reasonable way to pick which job to perform. He’s keeping the first station busy, and it’s similar to first-in, first-out scheduling. But of course, now everyone knows that you don’t release work based on the availability of the first station.

Instead, it should be based on the tempo of how quickly the bottleneck resource can consume the work.”

I just stare at him blankly.

He continues, “Because of how Mark was releasing work, inventory kept piling up in front of our bottleneck, and jobs were never finished on time. Every day was an emergency. For years, we were awarded Best Customer of the Year from our air freight shipment company, because we were overnighting thousands of pounds of finished goods to angry customers almost every week.”

He pauses and then says emphatically, “Eliyahu M. Goldratt, who created the Theory of Constraints, showed us how any improvements made anywhere besides the bottleneck are an illusion. Astonishing, but true! Any improvement made after the bottleneck is useless, because it will always remain starved, waiting for work from the bottleneck. And any improvements made before the bottleneck merely results in more inventory piling up at the bottleneck.”

He continues, “In our case, our bottleneck was a heat treat oven, just like in Goldratt’s novel, The Goal. We also had paint-curing booths that later became constraints, too. By the time we froze the release of all new jobs, you couldn’t even see the bottleneck work centers because they were surrounded by huge piles of inventory. Even from up here!”

Шикарная аналогия!

Если ваша работа -- конвейер, а его узкое место печь, то:

Значит будем плясать от печки!

5. Начнем от печки

Давайте рассмотрим самый базовый бизнес-кейс единственного сервера:

Запускаем demoscale, передавая 100 сообщений от одного клиента C одному серверу S:

$ demoscale one 100 C/1/0 S/1/50
#CmdName	NumMsg	Time	RPS
one{1,1}	100	6.613s	15.121
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	1	0.000s
->S	1	0.050s	100	15.121	0.000s	0.000s	0.066s	15.139	[100 0 0]%

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

Для простоты положим, что:

Итого наша прибыль 3.0 туг/сек -- неплохо по нынешним временам!

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

$ demoscale one 100 C/1/0 S/2/50
one{1,2}	100	6.636s	15.070
C	1	0.000s
->S	2	0.050s	100	15.070	0.000s	0.000s	0.066s	30.162	[100 0 0]%

Опа!

Итого вместо прибыли троекратный убыток: 9.1 туг/сек!!!

Шарик... Ты меня понял!

Но беда не приходит одна. Прямо щас на один бедный сервер навалятся два клиента:

$ demoscale one 100 C/2/0 S/1/50
one{2,1}	100	6.639s	15.062
C	2	0.000s
->S	1	0.050s	100	15.062	0.000s	0.066s	0.066s	15.068	[1 99 0]%

Гы! А ведь хуже не стало.

Один сервер, 6.6 секунды, сотня сообщений -- прибыль есть! Те же самые 3.0 туг/сек.

Ладно, раз уж есть два клиента, давайте подложим два сервера:

$ demoscale one 100 C/2/0 S/2/50
one{2,2}	100	3.331s	30.020
C	2	0.000s
->S	2	0.050s	100	30.022	0.000s	0.000s	0.067s	30.042	[100 0 0]%

Ага!

И теперь наша прибыль 6.1 туг/сек -- развальцевали!

Не, ну вдумайтесь еще раз: хоть дополнительный сервер и удваивает наши расходы (бухгалтерия пьет валидол), но доходы все кроют с запасом!!!

И да, мы удвоили прибыли AWS, Azure, GCP… но никто не в обиде. Win-win situation.

ОКай, хорошо. А теперь объясните мне каждый вариант. Как-то все неожиданно.

  1. one{1,1}: Это старт, запомним базовые цифры.
  2. one{1,2}: Второй сервер добавлен зря. Т.к. единственный их клиент может отправить следующее синхронное сообщение не раньше, чем получит ответ на предыдущее! Тем самым, второй сервер никогда не работает параллельно. Второй сервер -- широкое место, его можно убрать.
  3. one{2,1}: Два клиента параллельно отправляют по 50 синхронный сообщений. Их последовательно обрабатывает один сервер. Это узкое место -- его нужно расширить!
  4. one{2,2}: Мы расширили узкое место. Два клиента параллельно отправляют сообщения. А два сервера их параллельно обрабатывают -- красота!
Ну, или проще: Резюмирую: Еще раз: вертикальное масштабирование уменьшает время обработки запроса. Горизонтальное масштабирование обрабатывает несколько запросов одновременно.

6. Практика масштабирования

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

Ну как, реальные. Использовать мы будем demoscale -- специально написанную мною программу. Ее особенности в том, что:

Для удобства, и клиент и все серверы настраиваются с помощью одинакового выражения Name/NumInst/Slp (например, S1/2/30), где: Ну что, поехали!

6.1. Масштабирование list

Три сервера друг за другом. RPS каждого сервера ограничивается предыдущим (requests per second, пропускная способность).

Первый запуск:

$ demoscale list 400 C/8/0 S1/1/30 S2/1/40 S3/1/50
#CmdName	NumMsg	Time	RPS
list{8,1,1,1}	400	70.203s	5.698
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S1	1	0.030s	400	5.698	0.818s	0.345s	0.175s	5.698	[1 2 97]%
S2	1	0.040s	400	5.701	0.000s	0.000s	0.127s	7.849	[100 0 0]%
S3	1	0.050s	400	5.706	0.000s	0.000s	0.064s	15.643	[100 0 0]%

Мы видим, что:

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

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

Добавляем:

$ demoscale list 400 C/8/0 S1/2/30 S2/1/40 S3/1/50
list{8,2,1,1}	400	52.989s	7.549
C	8	0.000s
->S1	2	0.030s	400	7.549	0.511s	0.262s	0.265s	7.557	[1 0 99]%
S2	1	0.040s	400	7.555	0.000s	0.069s	0.132s	7.555	[48 52 0]%
S3	1	0.050s	400	7.563	0.000s	0.000s	0.069s	14.499	[100 0 0]%

О! 52.989 секунды вместо 70.203 -- это уже хорошо. Это 75%, что меньше на четверть.

Что дальше?

Формально говоря, красная очередь [1 0 99] первого сервера нам четко показывает, что он все еще bottleneck. Но! Очередь второго сервера [48 52 0] вдруг пожелтела -- самое время пристально на него взглянуть.

RPS(W) второго сервера 7.555, а у первого -- 7.557, т.е. S1 может работать чуть-чуть быстрее S2. Две тысячные секунды конечно ничтожная мелочь, но для нашего обучения пусть это станет сигналом.

Итак, раз S2 самый медленный, давайте ему поможем:

$ demoscale list 400 C/8/0 S1/2/30 S2/2/40 S3/1/50
list{8,2,2,1}	400	36.293s	11.021
C	8	0.000s
->S1	2	0.030s	400	11.021	0.342s	0.175s	0.180s	11.117	[3 1 96]%
S2	2	0.040s	400	11.035	0.000s	0.000s	0.132s	15.177	[100 0 0]%
S3	1	0.050s	400	11.053	0.000s	0.000s	0.069s	14.505	[100 0 0]%

Не зря потратились! 36.293 секунды выглядят еще лучше.

Но остался еще один инстанс -- куда бы его воткнуть? Правильно, ставим на красное!

$ demoscale list 400 C/8/0 S1/3/30 S2/2/40 S3/1/50
list{8,3,2,1}	400	25.867s	15.463
C	8	0.000s
->S1	3	0.030s	400	15.463	0.185s	0.127s	0.193s	15.547	[1 1 98]%
S2	2	0.040s	400	15.489	0.000s	0.001s	0.129s	15.558	[98 2 0]%
S3	1	0.050s	400	15.525	0.000s	0.001s	0.064s	15.574	[99 1 0]%

25.867 секунды!!! У Шарика будет фоторужье.

А теперь и не страшно всТупить на путь наименьшего сопротивления: ща тупо докинем по инстансу каждому серверу:

$ demoscale list 400 C/8/0 S1/2/30 S2/2/40 S3/2/50
list{8,2,2,2}	400	36.293s	11.021
C	8	0.000s
->S1	2	0.030s	400	11.021	0.340s	0.180s	0.181s	11.036	[1 0 99]%
S2	2	0.040s	400	11.034	0.000s	0.000s	0.133s	15.054	[100 0 0]%
S3	2	0.050s	400	11.052	0.000s	0.000s	0.069s	28.896	[100 0 0]%

36.293 секунды -- считай то же самое, что и у list{8,2,2,1}. Но тот не использует лишний инстанс!

Хотя ничего удивительного: дополнительный инстанс S3 поднял его RPS(W) с 14.505 до 28.896, но в этом нет никакого смысла, т.к. всех ограничивает S1 (значением возле 11).

А, чуть не забыл!

Вам конечно же интересно взглянуть, как оно там внутри... Не интересно? А я все равно расскажу!

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

$ demoscale -msg list 16 C/8/0 S1/2/30 S2/2/40 S3/2/50
1713042911010 > C[2] -> S1	0
1713042911027 > S1[0] -> S2
1713042911043 > S2[0] -> S3
1713042911108 < S3[1]	0
1713042911140 < S2[0]	0
1713042911173 < S1[0]	0
...
1713042912352 > C[4] -> S1	1
1713042912373 > S1[0] -> S2
1713042912394 > S2[0] -> S3
1713042912458 < S3[1]	8
1713042912500 < S2[0]	8
1713042912527 < S1[0]	8

Ну и что мы тут видим? А все просто как грабли:

  1. В момент времени 1713042911010 второй инстанс клиента C[2] отправил серверу S1 запрос номер 0.
  2. В 1713042911027 инстанс S1[0] отправил S2 запрос.
  3. В 1713042911043 инстанс S2[0] отправил S3 запрос.
  4. В 1713042911108 инстанс S3[1] отправил ответ номер 0.
  5. В 1713042911140 инстанс S2[0] отправил ответ номер 0.
  6. В 1713042911173 инстанс S1[0] отправил ответ номер 0.
Ай, ну картинка и так нам показывает, кто кому отправляет... Ха! Все дело в тайминге.

Например, даже Печкин вам скажет, что время обработки сообщения = время ожидания + время работы. А по цифрам выходит, что RPS не зависит от Wait1 и Wait2...

Так не бывает!

Гы, а вы сами взгляните на тайминги.


6.2. Масштабирование tree

Те же три сервера, но фронт ближе к бэкам. Как следствие, SL уже не ограничивает SR, т.к. не вызывает.

Первый запуск:

$ demoscale tree 400 C/8/0 S/1/30 SL/1/40 SR/1/50
#CmdName	NumMsg	Time	RPS
tree{8,1,1,1}	400	71.168s	5.621
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	5.621	0.829s	0.352s	0.178s	5.621	[1 0 99]%
SL	1	0.040s	400	5.629	0.000s	0.000s	0.064s	15.617	[100 0 0]%
SR	1	0.050s	400	5.628	0.000s	0.000s	0.065s	15.314	[100 0 0]%

71.168 секунды, все очень похоже. Но посмотрите на Work:

list	tree
0.175s	0.178s
0.127s	0.064s
0.064s	0.065s

Время обработки сообщения первыми и последними серверами практически не отличается. Но у S2 к нему добавилось S3, а у SL, конечно же, не добавилось.

Ну и нам давно интересно, чем же чудит рантайм Go. ОКай, обратите внимание, что Slp серверов SL и SR было задано как 0.040 и 0.050 соответственно. Но время работы Work у них идентично: 0.064 и 0.065. Так быть не должно!

Наш следующий шаг? Да тут без вариантов:

$ demoscale tree 400 C/8/0 S/2/30 SL/1/40 SR/1/50
tree{8,2,1,1}	400	38.780s	10.315
C	8	0.000s
->S	2	0.030s	400	10.315	0.371s	0.192s	0.193s	10.355	[1 0 99]%
SL	1	0.040s	400	10.345	0.000s	0.000s	0.064s	15.619	[100 0 0]%
SR	1	0.050s	400	10.344	0.000s	0.001s	0.065s	15.268	[99 1 0]%

Ого! Сразу стало 38.780 секунды, т.е. 54%. Резкое облегчение!

Оно и понятно, если сравнить RPS(W) первых запусков:

list	tree
5.698	5.621
7.849	15.617
15.643	15.314

Сечете фишку??

Те же самые три сервера с полностью идентичными настройками... Но масштабирование конфигурации tree сразу приносит на 37% больше дохода!!! Просто так, на ровном месте.

Вы это знали? Не верю.

Точнее, не верю, что вы могли это убедительно показать Шарику. Или Печкину. Короче, дево псам.

Итак, еще раз: выньте сервер из гмм... глубины!

Тащите поближе к фронту -- быстрее забегает. А если возможно, используйте параллельный вызов -- станет СОВСЕМ хорошо!

Гут. Вы пока думайте, а мы пойдем дальше:

$ demoscale tree 400 C/8/0 S/3/30 SL/1/40 SR/1/50
tree{8,3,1,1}	400	26.309s	15.204
C	8	0.000s
->S	3	0.030s	400	15.204	0.188s	0.128s	0.196s	15.309	[2 1 97]%
SL	1	0.040s	400	15.271	0.000s	0.001s	0.064s	15.702	[99 1 0]%
SR	1	0.050s	400	15.268	0.000s	0.003s	0.065s	15.343	[96 4 0]%

26.309 секунды на всего пяти инстансах! Прекрасно, просто прекрасно.

К тому же отметим практически одинаковый RPS(W): 15.309, 15.702 и 15.343. Значит дальше их расширять надо всех сразу, а у нас только инстанс в запасе. Ну ладно, потратим:

$ demoscale tree 400 C/8/0 S/4/30 SL/1/40 SR/1/50
tree{8,4,1,1}	400	26.145s	15.299
C	8	0.000s
->S	4	0.030s	400	15.299	0.127s	0.128s	0.260s	15.363	[2 0 98]%
SL	1	0.040s	400	15.370	0.000s	0.001s	0.064s	15.718	[99 1 0]%
SR	1	0.050s	400	15.368	0.000s	0.067s	0.065s	15.370	[1 95 4]%

О, желтенькая пошла!

Мы смогли перегрузить SR, но вообще-то геморрой не стоит свеч: 26.145 секунды вместо 26.309 -- это менее одного процента. А вот 6 инстансов вместо 5 -- это плюс 20 процентов затрат!

Лучше бы Шарику взяли пива. Не пьет? А мы и не заставляем.


6.3. Масштабирование rhomb

Четыре сервера, но один отдувается за двоих. Как это часто бывает в жизни.

Стартуем:

$ demoscale rhomb 400 C/8/0 S/1/30 SL/1/40 SR/1/45 SM/1/50
#CmdName		NumMsg	Time		RPS
rhomb{8,1,1,1,1}	400	121.242s	3.299
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	3.299	1.446s	0.596s	0.303s	3.299	[1 1 98]%
SL	1	0.040s	400	3.304	0.000s	0.000s	0.128s	7.838	[100 0 0]%
SR	1	0.045s	400	3.304	0.000s	0.000s	0.127s	7.845	[100 0 0]%
SM	1	0.050s	800	6.604	0.000s	0.000s	0.064s	15.654	[100 0 0]%

121.242 секунды не шутка! Ну так это ж на четверых.

И сразу отметим 800 сообщений у среднего сервера. Он же последний: SM. И конечно же в два раза больше RPS: 6.604.

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

RPS(W)? С ним тоже непросто: 15.654 было рассчитано на 800 сообщений, а не 400. И если, как мы привыкли, вы хотите сравнить его с остальными -- смело делите на два! Получается 7.827, что не так далеко от товарищей.

Гмм, все эти хитрые 6.604, 15.654... Неудобно же сравнивать! Почему б не нормировать всех по 400? Мой строгий друг, имей терпенье! Не вдруг приходит объясненье...

А пока добросим инстанс куда следует:

$ demoscale rhomb 400 C/8/0 S/2/30 SL/1/40 SR/1/45 SM/1/50
rhomb{8,2,1,1,1}	400	82.042s	4.876
C	8	0.000s
->S	2	0.030s	400	4.876	0.773s	0.402s	0.409s	4.890	[2 0 98]%
SL	1	0.040s	400	4.886	0.000s	0.022s	0.194s	5.157	[89 11 0]%
SR	1	0.045s	400	4.885	0.000s	0.005s	0.138s	7.243	[97 3 0]%
SM	1	0.050s	800	9.763	0.000s	0.029s	0.069s	14.481	[71 29 0]%

82.042 секунды, 68% -- не худший вариант.

Добавим красному:

$ demoscale rhomb 400 C/8/0 S/3/30 SL/1/40 SR/1/45 SM/1/50
rhomb{8,3,1,1,1}	400	54.341s	7.361
C	8	0.000s
->S	3	0.030s	400	7.361	0.384s	0.262s	0.404s	7.428	[4 0 96]%
SL	1	0.040s	400	7.384	0.000s	0.003s	0.135s	7.431	[98 2 0]%
SR	1	0.045s	400	7.384	0.000s	0.069s	0.135s	7.428	[49 51 0]%
SM	1	0.050s	800	14.750	0.000s	0.001s	0.067s	14.888	[98 2 0]%

О! И красному зашло, и желтых стало меньше.

Но нормированный RPS(W) теперь практически одинаковый: 7.428, 7.431, 7.428 и 7.444 -- если сыпать, то всем!

А у нас только инстанс в запасе, и его не хотелось бы проморгать. Не жалко? Бросаю:

$ demoscale rhomb 400 C/8/0 S/4/30 SL/1/40 SR/1/45 SM/1/50
rhomb{8,4,1,1,1}	400	53.830s	7.431
C	8	0.000s
->S	4	0.030s	400	7.431	0.260s	0.265s	0.534s	7.489	[2 0 98]%
SL	1	0.040s	400	7.455	0.000s	0.135s	0.134s	7.489	[1 98 1]%
SR	1	0.045s	400	7.455	0.000s	0.069s	0.133s	7.494	[49 51 0]%
SM	1	0.050s	800	14.890	0.000s	0.001s	0.067s	15.013	[99 1 0]%

53.830 секунды вместо 54.341, один процент выигрыша. Лучше Шарик нам снова проставится!


6.4. Масштабирование wshape

Группа пяти серверов, которую пользуют с двух сторон сразу! Нездоровая ситуация...

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

Задаем 800 сообщений на 16 инстансов клиента и любуемся:

$ demoscale wshape 800 C/16/0 SL/1/30 SR/1/35 SL2/1/40 SM/1/55 SR2/1/45
#CmdName		NumMsg	Time	RPS
wshape{16,1,1,1,1,1}	800	85.176s	9.392
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	16	0.000s
->SL	1	0.030s	400	4.708	0.800s	0.391s	0.208s	4.817	[7 3 90]%
->SR	1	0.035s	400	4.696	0.623s	0.348s	0.207s	4.830	[14 9 77]%
SL2	1	0.040s	400	4.715	0.000s	0.000s	0.068s	14.669	[100 0 0]%
SM	1	0.055s	800	9.406	0.000s	0.001s	0.073s	13.701	[99 1 0]%
SR2	1	0.045s	400	4.703	0.000s	0.000s	0.069s	14.545	[100 0 0]%

Это 85.176 секунды для начала. Просто запомним.

Что еще интересного?

Например, сервер SR, правая точка входа, сразу же в желтой зоне! Ну, эсеры -- они такие.

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

$ demoscale wshape 800 C/16/0 SL/2/30 SR/2/35 SL2/1/40 SM/1/55 SR2/1/45
wshape{16,2,2,1,1,1}	800	65.222s	12.266
C	16	0.000s
->SL	2	0.030s	400	6.153	0.601s	0.299s	0.313s	6.380	[7 2 91]%
->SR	2	0.035s	400	6.133	0.446s	0.284s	0.314s	6.376	[11 4 85]%
SL2	1	0.040s	400	6.165	0.000s	0.000s	0.066s	15.203	[100 0 0]%
SM	1	0.055s	800	12.289	0.000s	0.109s	0.079s	12.580	[6 55 39]%
SR2	1	0.045s	400	6.145	0.000s	0.000s	0.066s	15.170	[100 0 0]%

65.222 секунды -- во-первых, это красиво. А еще 77% -- изящно, но многовато!

Так, а что это там пожелтело в самом центре скульптурной группы? Ага, это товарищ SM с его 12.580 RPS(W). Делите надвое такие заявления: 6.290 -- это меньше всех остальных. Ща и ему поможем:

$ demoscale wshape 800 C/16/0 SL/2/30 SR/2/35 SL2/1/40 SM/2/55 SR2/1/45
wshape{16,2,2,1,2,1}	800	44.407s	18.015
C	16	0.000s
->SL	2	0.030s	400	9.008	0.123s	0.130s	0.207s	9.656	[39 5 56]%
->SR	2	0.035s	400	9.044	0.510s	0.189s	0.211s	9.490	[13 3 84]%
SL2	1	0.040s	400	9.030	0.000s	0.000s	0.065s	15.409	[100 0 0]%
SM	2	0.055s	800	18.033	0.000s	0.001s	0.079s	25.203	[98 2 0]%
SR2	1	0.045s	400	9.070	0.000s	0.001s	0.065s	15.463	[99 1 0]%

Ну вот, полегчало.

Но теперь напустили на линии фронта... Не бойтесь, ребята! У нас тут как раз завалялось два инстанса:

$ demoscale wshape 800 C/16/0 SL/3/30 SR/3/35 SL2/1/40 SM/2/55 SR2/1/45
wshape{16,3,3,1,2,1}	800	33.638s	23.783
C	16	0.000s
->SL	3	0.030s	400	11.967	0.221s	0.152s	0.239s	12.553	[9 1 90]%
->SR	3	0.035s	400	11.891	0.183s	0.135s	0.240s	12.523	[17 5 78]%
SL2	1	0.040s	400	12.013	0.000s	0.002s	0.063s	15.883	[98 2 0]%
SM	2	0.055s	800	23.870	0.000s	0.032s	0.080s	24.973	[44 35 21]%
SR2	1	0.045s	400	11.936	0.000s	0.001s	0.063s	15.769	[99 1 0]%

33.638 секунды -- непревзойденный результат! Ну, тупо не будет лучше:

$ demoscale wshape 800 C/16/0 SL/2/30 SR/2/35 SL2/2/40 SM/2/55 SR2/2/45
wshape{16,2,2,2,2,2}	800	44.166s	18.114
C	16	0.000s
->SL	2	0.030s	400	9.057	0.216s	0.160s	0.205s	9.736	[25 5 70]%
->SR	2	0.035s	400	9.098	0.491s	0.194s	0.208s	9.612	[11 2 87]%
SL2	2	0.040s	400	9.083	0.000s	0.000s	0.065s	30.662	[100 0 0]%
SM	2	0.055s	800	18.131	0.000s	0.002s	0.077s	26.002	[97 3 0]%
SR2	2	0.045s	400	9.124	0.000s	0.000s	0.066s	30.379	[100 0 0]%

Как можно видеть, это практически wshape{16,2,2,1,2,1}, но с двумя лишними инстансами. Вы правы, они лишние, т.к. SL2 и SR2 прекрасно справлялись с нагрузкой.

ОКай, все хорошо и ясно, но почему же мы все-таки не нормируем RPS под одинаковое количество сообщений? А потому, что, вообще говоря, наши серверы может дергать и кто-то другой! Да еще где-нибудь в середине... Тем самым критически важно увидеть первичные данные.


6.5. Итоги

Итак, как же мы все-таки масштабировали?

У нас с самого начала была какая-то тактика, и мы ее придерживались.

Первый пункт тактики -- понимание Модели Конвейера:

  1. Реальная скорость работы конвейера задается скоростью самой медленной операции! Точно так же, как скорость эскадры определяется скоростью самого медленного корабля. Точка.
  2. Если хотите ускорить конвейер -- ускорьте узкое место! Ускорение других операций ничего вам не даст.
  3. Узкое место конвейера очень легко обнаружить -- это груда необработанных деталей перед самой медленной операцией.
Второй пункт тактики -- превращение серверов в знакомый конвейер: Но главный пункт -- не фантазируйте! А реально смотрите на Факты: Как вы уже поняли, пригодные для горизонтального масштабирования серверы должны проектироваться с рождения! Это не дело случая. Как я уже писал в другой статье: Многопоточное программирование определяется дизайном приложения, который РАЗРЕШАЕТ параллельное одновременное исполнение.

Та же фраза справедлива и для серверов:

ОКай, если в некотором учреждении увеличение количества инстансов не приносит большого дохода -- может лучше убрать тогда лишние и сэкономить большие расходы?

Отличная идея! Просто прекрасная, с технической точки зрения.

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


7. Программа demoscale

Суха теория, мой друг, а древо серверов весь список обгоняет.

demoscale -- это демонстрационная программа для изучения горизонтального масштабирования. Она написана на Go и легко адаптируется под ваши нужды.

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

При вызове без параметров (или с неправильными параметрами) она выдает подсказку:

$ demoscale
Usage:
demoscale one 8 C/4/0 S/1/50
demoscale list 16 C/8/0 S1/1/30 S2/1/40 S3/1/50
demoscale tree 16 C/8/0 S/1/30 SL/1/40 SR/1/50
demoscale rhomb 16 C/8/0 S/1/30 SL/1/40 SR/1/45 SM/1/50
demoscale wshape 32 C/16/0 SL/1/30 SR/1/35 SL2/1/40 SM/1/55 SR2/1/45
demoscale one all
demoscale list all
demoscale tree all
demoscale rhomb all
demoscale wshape all
demoscale other

Таким образом, у нас есть два способа вызова каждой конфигурации:

  1. demoscale Cmd NumMsg ClnArg SrvArg [SrvArg2 ...]
  2. demoscale Cmd all
Где: И ClnArg и SrvArg задаются одним и тем же выражением Name/NumInst/Slp (например, S/1/50), где: Первый способ вызывает demoscale для конкретного набора инстансов. А второй -- прогоняет сразу несколько наборов для сравнения.

В любом случае, результат будет выглядеть следующим образом:

#CmdName		NumMsg	Time	RPS
wshape{16,1,1,1,1,1}	32	3.296s	9.709
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	16	0.000s
->SL	1	0.030s	16	4.854	0.481s	0.370s	0.206s	4.854	[7 6 87]%
->SR	1	0.035s	16	4.886	0.475s	0.340s	0.203s	4.918	[14 7 79]%
SL2	1	0.040s	16	5.116	0.000s	0.000s	0.063s	15.760	[100 0 0]%
SM	1	0.055s	32	9.894	0.000s	0.003s	0.075s	13.406	[97 3 0]%
SR2	1	0.045s	16	5.089	0.000s	0.000s	0.069s	14.510	[100 0 0]%

Где:

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

Но обратите внимание: рандомизация задержки ответа плюс непредсказуемость рантайма Go могут заметно повлиять на результат! Если видите "что-то не то" -- повторите еще пару раз. Так скажем, 5% туды-сюды можно смело игнорировать.


7.1. Конфигурация one

Всего один сервер.

$ demoscale one all
#CmdName	NumMsg	Time	RPS
one{1,1}	200	13.177s	15.178
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	1	0.000s
->S	1	0.050s	200	15.178	0.000s	0.000s	0.066s	15.186	[100 0 0]%
one{1,2}	200	13.152s	15.207
C	1	0.000s
->S	2	0.050s	200	15.207	0.000s	0.000s	0.066s	30.420	[100 0 0]%
one{2,1}	200	13.202s	15.149
C	2	0.000s
->S	1	0.050s	200	15.149	0.000s	0.066s	0.066s	15.152	[1 99 0]%
one{2,2}	200	6.917s	28.913
C	2	0.000s
->S	2	0.050s	200	28.913	0.000s	0.000s	0.069s	29.013	[100 0 0]%
#46.4488344s
$ demoscale++ one all
#CmdName	NumMsg	Time	RPS
one{1,1}	200	12.795s	15.631
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	1	0.000s
->S	1	0.050s	200	15.632	0.000s	0.000s	0.064s	15.662	[100 0 0]%
one{1,2}	200	12.657s	15.802
C	1	0.000s
->S	2	0.050s	200	15.803	0.000s	0.000s	0.063s	31.679	[100 0 0]%
one{2,1}	200	12.629s	15.836
C	2	0.000s
->S	1	0.050s	200	15.837	0.000s	0.063s	0.063s	15.843	[1 99 0]%
one{2,2}	200	6.339s	31.549
C	2	0.000s
->S	2	0.050s	200	31.554	0.000s	0.000s	0.063s	31.613	[100 0 0]%
#44.423s


7.2. Конфигурация list

Три сервера друг за другом.

$ demoscale list all
#CmdName	NumMsg	Time	RPS
list{8,1,1,1}	400	71.251s	5.614
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S1	1	0.030s	400	5.614	0.852s	0.354s	0.178s	5.614	[0 1 99]%
S2	1	0.040s	400	5.618	0.000s	0.000s	0.130s	7.707	[100 0 0]%
S3	1	0.050s	400	5.622	0.000s	0.000s	0.065s	15.299	[100 0 0]%
list{8,2,1,1}	400	53.420s	7.488
C	8	0.000s
->S1	2	0.030s	400	7.488	0.515s	0.265s	0.267s	7.496	[0 1 99]%
S2	1	0.040s	400	7.494	0.000s	0.070s	0.133s	7.495	[47 53 0]%
S3	1	0.050s	400	7.503	0.000s	0.000s	0.069s	14.489	[100 0 0]%
list{8,2,2,1}	400	36.644s	10.916
C	8	0.000s
->S1	2	0.030s	400	10.916	0.347s	0.178s	0.182s	11.011	[2 1 97]%
S2	2	0.040s	400	10.930	0.000s	0.000s	0.132s	15.100	[100 0 0]%
S3	1	0.050s	400	10.949	0.000s	0.000s	0.069s	14.404	[100 0 0]%
list{8,2,2,2}	400	35.686s	11.209
C	8	0.000s
->S1	2	0.030s	400	11.209	0.335s	0.173s	0.178s	11.224	[2 2 96]%
S2	2	0.040s	400	11.224	0.000s	0.000s	0.129s	15.453	[100 0 0]%
S3	2	0.050s	400	11.244	0.000s	0.000s	0.065s	30.578	[100 0 0]%
list{8,3,1,1}	400	53.520s	7.474
C	8	0.000s
->S1	3	0.030s	400	7.474	0.394s	0.265s	0.401s	7.491	[1 0 99]%
S2	1	0.040s	400	7.481	0.000s	0.203s	0.134s	7.481	[0 48 52]%
S3	1	0.050s	400	7.489	0.000s	0.000s	0.069s	14.403	[100 0 0]%
list{8,3,2,1}	400	26.347s	15.182
C	8	0.000s
->S1	3	0.030s	400	15.182	0.192s	0.129s	0.197s	15.248	[1 1 98]%
S2	2	0.040s	400	15.210	0.000s	0.002s	0.131s	15.262	[97 3 0]%
S3	1	0.050s	400	15.248	0.000s	0.002s	0.065s	15.277	[98 2 0]%
#4m36.8695941s
$ demoscale++ list all
#CmdName	NumMsg	Time	RPS
list{8,1,1,1}	400	67.612s	5.916
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S1	1	0.030s	400	5.916	0.799s	0.332s	0.169s	5.917	[1 1 98]%
S2	1	0.040s	400	5.919	0.000s	0.000s	0.121s	8.233	[100 0 0]%
S3	1	0.050s	400	5.925	0.000s	0.000s	0.063s	15.853	[100 0 0]%
list{8,2,1,1}	400	48.663s	8.220
C	8	0.000s
->S1	2	0.030s	400	8.220	0.459s	0.241s	0.243s	8.232	[0 1 99]%
S2	1	0.040s	400	8.227	0.000s	0.074s	0.122s	8.229	[39 61 0]%
S3	1	0.050s	400	8.238	0.000s	0.000s	0.063s	15.861	[100 0 0]%
list{8,2,2,1}	400	33.809s	11.831
C	8	0.000s
->S1	2	0.030s	400	11.832	0.316s	0.165s	0.169s	11.858	[1 2 97]%
S2	2	0.040s	400	11.854	0.000s	0.000s	0.121s	16.496	[100 0 0]%
S3	1	0.050s	400	11.878	0.000s	0.001s	0.063s	15.874	[99 1 0]%
list{8,2,2,2}	400	33.928s	11.790
C	8	0.000s
->S1	2	0.030s	400	11.790	0.312s	0.166s	0.169s	11.854	[2 1 97]%
S2	2	0.040s	400	11.806	0.000s	0.000s	0.121s	16.500	[100 0 0]%
S3	2	0.050s	400	11.829	0.000s	0.000s	0.063s	31.740	[100 0 0]%
list{8,3,1,1}	400	48.700s	8.214
C	8	0.000s
->S1	3	0.030s	400	8.214	0.341s	0.237s	0.361s	8.315	[3 0 97]%
S2	1	0.040s	400	8.221	0.000s	0.192s	0.121s	8.247	[2 38 60]%
S3	1	0.050s	400	8.232	0.000s	0.000s	0.063s	15.873	[100 0 0]%
list{8,3,2,1}	400	25.542s	15.660
C	8	0.000s
->S1	3	0.030s	400	15.661	0.173s	0.123s	0.190s	15.770	[2 3 95]%
S2	2	0.040s	400	15.688	0.000s	0.016s	0.127s	15.769	[75 25 0]%
S3	1	0.050s	400	15.727	0.000s	0.006s	0.063s	15.830	[91 9 0]%
#258.265s


7.3. Конфигурация tree

Три сервера деревом.

$ demoscale tree all
#CmdName	NumMsg	Time	RPS
tree{8,1,1,1}	400	70.995s	5.634
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	5.634	0.845s	0.352s	0.177s	5.634	[0 1 99]%
SL	1	0.040s	400	5.644	0.000s	0.000s	0.064s	15.690	[100 0 0]%
SR	1	0.050s	400	5.643	0.000s	0.000s	0.065s	15.318	[100 0 0]%
tree{8,2,1,1}	400	38.908s	10.281
C	8	0.000s
->S	2	0.030s	400	10.281	0.367s	0.191s	0.194s	10.335	[1 1 98]%
SL	1	0.040s	400	10.310	0.000s	0.000s	0.064s	15.561	[100 0 0]%
SR	1	0.050s	400	10.311	0.000s	0.001s	0.066s	15.183	[99 1 0]%
tree{8,2,1,2}	400	38.643s	10.351
C	8	0.000s
->S	2	0.030s	400	10.351	0.374s	0.191s	0.192s	10.391	[1 0 99]%
SL	1	0.040s	400	10.381	0.000s	0.000s	0.064s	15.541	[100 0 0]%
SR	2	0.050s	400	10.381	0.000s	0.000s	0.065s	30.566	[100 0 0]%
tree{8,2,2,1}	400	38.711s	10.333
C	8	0.000s
->S	2	0.030s	400	10.333	0.376s	0.192s	0.193s	10.341	[0 1 99]%
SL	2	0.040s	400	10.364	0.000s	0.000s	0.064s	31.021	[100 0 0]%
SR	1	0.050s	400	10.363	0.000s	0.001s	0.065s	15.329	[99 1 0]%
tree{8,2,2,2}	400	38.083s	10.503
C	8	0.000s
->S	2	0.030s	400	10.503	0.365s	0.188s	0.190s	10.520	[1 0 99]%
SL	2	0.040s	400	10.535	0.000s	0.000s	0.064s	31.287	[100 0 0]%
SR	2	0.050s	400	10.534	0.000s	0.000s	0.065s	30.543	[100 0 0]%
tree{8,3,1,1}	400	26.460s	15.117
C	8	0.000s
->S	3	0.030s	400	15.117	0.187s	0.128s	0.197s	15.261	[3 1 96]%
SL	1	0.040s	400	15.182	0.000s	0.001s	0.063s	15.849	[99 1 0]%
SR	1	0.050s	400	15.182	0.000s	0.005s	0.065s	15.296	[93 7 0]%
tree{8,3,1,2}	400	25.692s	15.569
C	8	0.000s
->S	3	0.030s	400	15.569	0.185s	0.126s	0.192s	15.605	[2 0 98]%
SL	1	0.040s	400	15.640	0.000s	0.001s	0.064s	15.734	[99 1 0]%
SR	2	0.050s	400	15.637	0.000s	0.000s	0.065s	30.628	[100 0 0]%
tree{8,3,2,1}	400	26.004s	15.382
C	8	0.000s
->S	3	0.030s	400	15.382	0.191s	0.129s	0.195s	15.418	[1 0 99]%
SL	2	0.040s	400	15.452	0.000s	0.000s	0.064s	31.354	[100 0 0]%
SR	1	0.050s	400	15.450	0.000s	0.002s	0.065s	15.461	[97 3 0]%
tree{8,4,1,1}	400	26.170s	15.285
C	8	0.000s
->S	4	0.030s	400	15.285	0.126s	0.128s	0.259s	15.419	[2 1 97]%
SL	1	0.040s	400	15.350	0.000s	0.005s	0.063s	15.810	[93 6 1]%
SR	1	0.050s	400	15.348	0.000s	0.063s	0.065s	15.415	[8 88 4]%
#5m29.6673422s
$ demoscale++ tree all
#CmdName	NumMsg	Time	RPS
tree{8,1,1,1}	400	67.537s	5.923
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	5.923	0.787s	0.335s	0.169s	5.924	[0 1 99]%
SL	1	0.040s	400	5.932	0.000s	0.000s	0.058s	17.242	[100 0 0]%
SR	1	0.050s	400	5.933	0.000s	0.000s	0.063s	15.888	[100 0 0]%
tree{8,2,1,1}	400	34.220s	11.689
C	8	0.000s
->S	2	0.030s	400	11.689	0.303s	0.162s	0.170s	11.792	[5 0 95]%
SL	1	0.040s	400	11.731	0.000s	0.000s	0.058s	17.256	[100 0 0]%
SR	1	0.050s	400	11.726	0.000s	0.001s	0.063s	15.913	[99 1 0]%
tree{8,2,1,2}	400	34.452s	11.610
C	8	0.000s
->S	2	0.030s	400	11.611	0.311s	0.165s	0.170s	11.772	[3 2 95]%
SL	1	0.040s	400	11.646	0.000s	0.001s	0.058s	17.224	[98 2 0]%
SR	2	0.050s	400	11.646	0.000s	0.000s	0.063s	31.799	[100 0 0]%
tree{8,2,2,1}	400	34.128s	11.721
C	8	0.000s
->S	2	0.030s	400	11.721	0.310s	0.165s	0.170s	11.769	[1 4 95]%
SL	2	0.040s	400	11.758	0.000s	0.000s	0.058s	34.517	[100 0 0]%
SR	1	0.050s	400	11.758	0.000s	0.001s	0.063s	15.880	[98 2 0]%
tree{8,2,2,2}	400	33.621s	11.897
C	8	0.000s
->S	2	0.030s	400	11.897	0.303s	0.166s	0.168s	11.901	[1 1 98]%
SL	2	0.040s	400	11.935	0.000s	0.000s	0.057s	34.845	[100 0 0]%
SR	2	0.050s	400	11.935	0.000s	0.000s	0.064s	31.484	[100 0 0]%
tree{8,3,1,1}	400	25.662s	15.587
C	8	0.000s
->S	3	0.030s	400	15.588	0.178s	0.122s	0.189s	15.835	[4 1 95]%
SL	1	0.040s	400	15.653	0.000s	0.001s	0.058s	17.187	[99 1 0]%
SR	1	0.050s	400	15.653	0.000s	0.020s	0.063s	15.854	[69 31 0]%
tree{8,3,1,2}	400	23.716s	16.866
C	8	0.000s
->S	3	0.030s	400	16.867	0.170s	0.116s	0.177s	16.911	[2 0 98]%
SL	1	0.040s	400	16.943	0.000s	0.009s	0.058s	17.264	[85 15 0]%
SR	2	0.050s	400	16.943	0.000s	0.000s	0.063s	31.802	[100 0 0]%
tree{8,3,2,1}	400	25.469s	15.705
C	8	0.000s
->S	3	0.030s	400	15.706	0.180s	0.123s	0.189s	15.852	[3 2 95]%
SL	2	0.040s	400	15.773	0.000s	0.000s	0.058s	34.637	[100 0 0]%
SR	1	0.050s	400	15.773	0.000s	0.021s	0.063s	15.878	[67 33 0]%
tree{8,4,1,1}	400	25.547s	15.658
C	8	0.000s
->S	4	0.030s	400	15.658	0.121s	0.122s	0.252s	15.881	[4 1 95]%
SL	1	0.040s	400	15.724	0.000s	0.003s	0.058s	17.149	[96 3 1]%
SR	1	0.050s	400	15.725	0.000s	0.080s	0.063s	15.859	[5 65 30]%
#304.370s


7.4. Конфигурация rhomb

Четыре сервера ромбом.

$ demoscale rhomb all
#CmdName		NumMsg	Time		RPS
rhomb{8,1,1,1,1}	400	123.320s	3.244
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	3.244	1.461s	0.610s	0.308s	3.244	[1 0 99]%
SL	1	0.040s	400	3.248	0.000s	0.000s	0.130s	7.715	[100 0 0]%
SR	1	0.045s	400	3.248	0.000s	0.000s	0.130s	7.685	[100 0 0]%
SM	1	0.050s	800	6.493	0.000s	0.000s	0.065s	15.293	[100 0 0]%
rhomb{8,2,1,1,1}	400	82.261s	4.863
C	8	0.000s
->S	2	0.030s	400	4.863	0.783s	0.403s	0.410s	4.878	[2 0 98]%
SL	1	0.040s	400	4.873	0.000s	0.021s	0.197s	5.083	[90 10 0]%
SR	1	0.045s	400	4.873	0.000s	0.004s	0.138s	7.243	[98 2 0]%
SM	1	0.050s	800	9.739	0.000s	0.031s	0.070s	14.365	[70 30 0]%
rhomb{8,2,1,1,2}	400	66.999s	5.970
C	8	0.000s
->S	2	0.030s	400	5.970	0.638s	0.330s	0.333s	6.003	[1 1 98]%
SL	1	0.040s	400	5.986	0.000s	0.010s	0.135s	7.431	[94 6 0]%
SR	1	0.045s	400	5.986	0.000s	0.000s	0.132s	7.579	[100 0 0]%
SM	2	0.050s	800	11.961	0.000s	0.000s	0.067s	29.805	[100 0 0]%
rhomb{8,2,1,2,1}	400	81.470s	4.910
C	8	0.000s
->S	2	0.030s	400	4.910	0.765s	0.401s	0.407s	4.915	[1 1 98]%
SL	1	0.040s	400	4.921	0.000s	0.022s	0.198s	5.063	[89 11 0]%
SR	2	0.045s	400	4.920	0.000s	0.000s	0.137s	14.572	[100 0 0]%
SM	1	0.050s	800	9.833	0.000s	0.031s	0.069s	14.433	[70 30 0]%
rhomb{8,2,2,1,1}	400	76.962s	5.197
C	8	0.000s
->S	2	0.030s	400	5.197	0.739s	0.380s	0.384s	5.204	[1 0 99]%
SL	2	0.040s	400	5.209	0.000s	0.000s	0.137s	14.632	[100 0 0]%
SR	1	0.045s	400	5.209	0.000s	0.035s	0.157s	6.375	[82 18 0]%
SM	1	0.050s	800	10.410	0.000s	0.012s	0.069s	14.497	[88 12 0]%
rhomb{8,3,1,1,1}	400	55.064s	7.264
C	8	0.000s
->S	3	0.030s	400	7.264	0.402s	0.273s	0.412s	7.282	[1 0 99]%
SL	1	0.040s	400	7.297	0.000s	0.011s	0.137s	7.298	[93 6 1]%
SR	1	0.045s	400	7.286	0.000s	0.067s	0.134s	7.448	[51 49 0]%
SM	1	0.050s	800	14.557	0.000s	0.003s	0.067s	14.884	[96 4 0]%
#8m6.0766549s
$ demoscale++ rhomb all
#CmdName		NumMsg	Time		RPS
rhomb{8,1,1,1,1}	400	118.037s	3.389
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	1	0.030s	400	3.389	1.330s	0.572s	0.295s	3.389	[2 2 96]%
SL	1	0.040s	400	3.393	0.000s	0.000s	0.121s	8.240	[100 0 0]%
SR	1	0.045s	400	3.393	0.000s	0.000s	0.126s	7.934	[100 0 0]%
SM	1	0.050s	800	6.783	0.000s	0.000s	0.063s	15.897	[100 0 0]%
rhomb{8,2,1,1,1}	400	74.684s	5.356
C	8	0.000s
->S	2	0.030s	400	5.356	0.667s	0.366s	0.373s	5.363	[0 4 96]%
SL	1	0.040s	400	5.368	0.000s	0.008s	0.149s	6.694	[96 4 0]%
SR	1	0.045s	400	5.368	0.000s	0.022s	0.146s	6.833	[88 12 0]%
SM	1	0.050s	800	10.727	0.000s	0.024s	0.063s	15.818	[74 26 0]%
rhomb{8,2,1,1,2}	400	59.263s	6.750
C	8	0.000s
->S	2	0.030s	400	6.750	0.554s	0.290s	0.296s	6.760	[2 0 98]%
SL	1	0.040s	400	6.769	0.000s	0.000s	0.121s	8.246	[100 0 0]%
SR	1	0.045s	400	6.769	0.000s	0.001s	0.126s	7.930	[99 1 0]%
SM	2	0.050s	800	13.523	0.000s	0.000s	0.063s	31.707	[100 0 0]%
rhomb{8,2,1,2,1}	400	72.736s	5.499
C	8	0.000s
->S	2	0.030s	400	5.499	0.650s	0.356s	0.362s	5.517	[2 0 98]%
SL	1	0.040s	400	5.512	0.000s	0.023s	0.145s	6.916	[87 13 0]%
SR	2	0.045s	400	5.512	0.000s	0.000s	0.147s	13.568	[100 0 0]%
SM	1	0.050s	800	11.014	0.000s	0.022s	0.063s	15.855	[75 25 0]%
rhomb{8,2,2,1,1}	400	72.236s	5.537
C	8	0.000s
->S	2	0.030s	400	5.537	0.641s	0.352s	0.358s	5.579	[2 1 97]%
SL	2	0.040s	400	5.550	0.000s	0.000s	0.139s	14.423	[100 0 0]%
SR	1	0.045s	400	5.550	0.000s	0.027s	0.145s	6.904	[85 15 0]%
SM	1	0.050s	800	11.091	0.000s	0.019s	0.063s	15.884	[79 21 0]%
rhomb{8,3,1,1,1}	400	51.197s	7.813
C	8	0.000s
->S	3	0.030s	400	7.813	0.364s	0.253s	0.383s	7.833	[1 1 98]%
SL	1	0.040s	400	7.850	0.000s	0.015s	0.127s	7.858	[89 11 0]%
SR	1	0.045s	400	7.840	0.000s	0.067s	0.126s	7.908	[48 52 0]%
SM	1	0.050s	800	15.661	0.000s	0.003s	0.063s	15.870	[95 5 0]%
#448.167s


7.5. Конфигурация wshape

Пять серверов с двумя точками входа.

$ demoscale wshape all
#CmdName		NumMsg	Time	RPS
wshape{16,1,1,1,1,1}	800	83.120s	9.625
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	16	0.000s
->SL	1	0.030s	400	4.812	0.835s	0.403s	0.207s	4.841	[3 1 96]%
->SR	1	0.035s	400	4.824	0.638s	0.392s	0.207s	4.842	[3 5 92]%
SL2	1	0.040s	400	4.820	0.000s	0.000s	0.067s	14.888	[100 0 0]%
SM	1	0.055s	800	9.630	0.000s	0.001s	0.075s	13.402	[99 1 0]%
SR2	1	0.045s	400	4.832	0.000s	0.000s	0.069s	14.573	[100 0 0]%
wshape{16,1,2,1,1,1}	800	85.885s	9.315
C	16	0.000s
->SL	1	0.030s	400	4.657	1.786s	0.406s	0.212s	4.715	[5 1 94]%
->SR	2	0.035s	400	4.668	0.006s	0.006s	0.208s	9.608	[98 1 1]%
SL2	1	0.040s	400	4.665	0.000s	0.000s	0.064s	15.562	[100 0 0]%
SM	1	0.055s	800	9.320	0.000s	0.007s	0.078s	12.895	[93 7 0]%
SR2	1	0.045s	400	4.675	0.000s	0.000s	0.065s	15.370	[100 0 0]%
wshape{16,2,1,1,1,1}	800	84.593s	9.457
C	16	0.000s
->SL	2	0.030s	400	4.740	0.006s	0.007s	0.213s	9.406	[98 1 1]%
->SR	1	0.035s	400	4.729	1.755s	0.399s	0.209s	4.787	[4 3 93]%
SL2	1	0.040s	400	4.746	0.000s	0.000s	0.064s	15.647	[100 0 0]%
SM	1	0.055s	800	9.472	0.000s	0.008s	0.078s	12.900	[93 7 0]%
SR2	1	0.045s	400	4.736	0.000s	0.000s	0.065s	15.405	[100 0 0]%
wshape{16,2,2,1,1,1}	800	64.551s	12.393
C	16	0.000s
->SL	2	0.030s	400	6.197	0.569s	0.307s	0.315s	6.349	[5 0 95]%
->SR	2	0.035s	400	6.216	0.447s	0.302s	0.315s	6.356	[5 3 92]%
SL2	1	0.040s	400	6.210	0.000s	0.000s	0.066s	15.214	[100 0 0]%
SM	1	0.055s	800	12.402	0.000s	0.111s	0.079s	12.615	[3 57 40]%
SR2	1	0.045s	400	6.229	0.000s	0.000s	0.066s	15.231	[100 0 0]%
wshape{16,2,2,1,1,2}	800	63.504s	12.598
C	16	0.000s
->SL	2	0.030s	400	6.299	0.615s	0.298s	0.310s	6.450	[6 0 94]%
->SR	2	0.035s	400	6.301	0.392s	0.281s	0.309s	6.468	[9 6 85]%
SL2	1	0.040s	400	6.316	0.000s	0.000s	0.066s	15.181	[100 0 0]%
SM	1	0.055s	800	12.610	0.000s	0.105s	0.078s	12.760	[5 58 37]%
SR2	2	0.045s	400	6.316	0.000s	0.000s	0.066s	30.203	[100 0 0]%
wshape{16,2,2,1,2,1}	800	44.585s	17.943
C	16	0.000s
->SL	2	0.030s	400	8.972	0.067s	0.119s	0.206s	9.722	[40 14 46]%
->SR	2	0.035s	400	9.008	0.592s	0.195s	0.209s	9.564	[12 1 87]%
SL2	1	0.040s	400	8.995	0.000s	0.000s	0.065s	15.478	[100 0 0]%
SM	2	0.055s	800	17.963	0.000s	0.001s	0.078s	25.529	[98 2 0]%
SR2	1	0.045s	400	9.038	0.000s	0.001s	0.065s	15.478	[99 1 0]%
wshape{16,2,2,1,2,2}	800	44.090s	18.145
C	16	0.000s
->SL	2	0.030s	400	9.072	0.204s	0.161s	0.206s	9.710	[25 4 71]%
->SR	2	0.035s	400	9.115	0.453s	0.195s	0.208s	9.601	[11 1 88]%
SL2	1	0.040s	400	9.102	0.000s	0.001s	0.064s	15.538	[99 1 0]%
SM	2	0.055s	800	18.165	0.000s	0.001s	0.079s	25.336	[98 2 0]%
SR2	2	0.045s	400	9.145	0.000s	0.000s	0.065s	30.968	[100 0 0]%
wshape{16,2,2,2,1,1}	800	63.574s	12.584
C	16	0.000s
->SL	2	0.030s	400	6.309	0.586s	0.303s	0.313s	6.388	[4 1 95]%
->SR	2	0.035s	400	6.292	0.459s	0.289s	0.313s	6.390	[8 2 90]%
SL2	2	0.040s	400	6.321	0.000s	0.000s	0.066s	30.478	[100 0 0]%
SM	1	0.055s	800	12.609	0.000s	0.109s	0.079s	12.678	[3 57 40]%
SR2	1	0.045s	400	6.304	0.000s	0.000s	0.066s	15.145	[100 0 0]%
wshape{16,2,2,2,1,2}	800	63.864s	12.527
C	16	0.000s
->SL	2	0.030s	400	6.283	0.542s	0.302s	0.313s	6.385	[4 2 94]%
->SR	2	0.035s	400	6.263	0.470s	0.286s	0.313s	6.394	[8 5 87]%
SL2	2	0.040s	400	6.296	0.000s	0.000s	0.066s	30.490	[100 0 0]%
SM	1	0.055s	800	12.552	0.000s	0.108s	0.079s	12.664	[3 58 39]%
SR2	2	0.045s	400	6.278	0.000s	0.000s	0.066s	30.320	[100 0 0]%
wshape{16,2,2,2,2,1}	800	44.170s	18.112
C	16	0.000s
->SL	2	0.030s	400	9.056	0.098s	0.123s	0.206s	9.723	[38 12 50]%
->SR	2	0.035s	400	9.097	0.543s	0.185s	0.210s	9.518	[14 4 82]%
SL2	2	0.040s	400	9.083	0.000s	0.000s	0.065s	30.906	[100 0 0]%
SM	2	0.055s	800	18.131	0.000s	0.002s	0.078s	25.585	[97 3 0]%
SR2	1	0.045s	400	9.124	0.000s	0.001s	0.065s	15.380	[99 1 0]%
wshape{16,2,2,2,2,2}	800	42.130s	18.989
C	16	0.000s
->SL	2	0.030s	400	9.540	0.117s	0.142s	0.205s	9.760	[30 5 65]%
->SR	2	0.035s	400	9.494	0.600s	0.194s	0.208s	9.622	[7 2 91]%
SL2	2	0.040s	400	9.571	0.000s	0.000s	0.066s	30.513	[100 0 0]%
SM	2	0.055s	800	19.048	0.000s	0.002s	0.075s	26.634	[96 4 0]%
SR2	2	0.045s	400	9.527	0.000s	0.000s	0.067s	29.965	[100 0 0]%
wshape{16,2,3,1,2,1}	800	44.549s	17.958
C	16	0.000s
->SL	2	0.030s	400	8.979	0.763s	0.192s	0.206s	9.728	[13 2 85]%
->SR	3	0.035s	400	9.017	0.005s	0.006s	0.208s	14.445	[97 0 3]%
SL2	1	0.040s	400	9.006	0.000s	0.000s	0.066s	15.198	[100 0 0]%
SM	2	0.055s	800	17.977	0.000s	0.002s	0.076s	26.148	[97 3 0]%
SR2	1	0.045s	400	9.048	0.000s	0.001s	0.066s	15.256	[99 1 0]%
wshape{16,3,2,1,2,1}	800	42.276s	18.923
C	16	0.000s
->SL	3	0.030s	400	9.462	0.005s	0.006s	0.208s	14.409	[97 1 2]%
->SR	2	0.035s	400	9.502	0.836s	0.203s	0.209s	9.555	[3 3 94]%
SL2	1	0.040s	400	9.487	0.000s	0.001s	0.066s	15.254	[99 1 0]%
SM	2	0.055s	800	18.945	0.000s	0.004s	0.076s	26.297	[93 7 0]%
SR2	1	0.045s	400	9.533	0.000s	0.001s	0.066s	15.247	[99 1 0]%
wshape{16,3,3,1,2,1}	800	32.127s	24.901
C	16	0.000s
->SL	3	0.030s	400	12.530	0.277s	0.143s	0.235s	12.770	[9 3 88]%
->SR	3	0.035s	400	12.451	0.116s	0.151s	0.236s	12.712	[5 2 93]%
SL2	1	0.040s	400	12.578	0.000s	0.004s	0.064s	15.617	[95 5 0]%
SM	2	0.055s	800	25.000	0.000s	0.025s	0.078s	25.493	[48 40 12]%
SR2	1	0.045s	400	12.500	0.000s	0.004s	0.064s	15.510	[95 5 0]%
#13m23.0205967s
$ demoscale++ wshape all
#CmdName		NumMsg	Time	RPS
wshape{16,1,1,1,1,1}	800	70.953s	11.275
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	16	0.000s
->SL	1	0.030s	400	5.651	0.776s	0.338s	0.174s	5.736	[4 1 95]%
->SR	1	0.035s	400	5.638	0.768s	0.329s	0.174s	5.736	[5 5 90]%
SL2	1	0.040s	400	5.659	0.000s	0.000s	0.058s	17.160	[100 0 0]%
SM	1	0.055s	800	11.291	0.000s	0.003s	0.064s	15.743	[97 3 0]%
SR2	1	0.045s	400	5.645	0.000s	0.000s	0.063s	15.826	[100 0 0]%
wshape{16,1,2,1,1,1}	800	69.980s	11.432
C	16	0.000s
->SL	1	0.030s	400	5.716	1.692s	0.343s	0.174s	5.731	[2 0 98]%
->SR	2	0.035s	400	5.730	0.005s	0.005s	0.174s	11.480	[98 1 1]%
SL2	1	0.040s	400	5.725	0.000s	0.000s	0.058s	17.232	[100 0 0]%
SM	1	0.055s	800	11.439	0.000s	0.003s	0.063s	15.825	[97 3 0]%
SR2	1	0.045s	400	5.739	0.000s	0.000s	0.063s	15.918	[100 0 0]%
wshape{16,2,1,1,1,1}	800	69.905s	11.444
C	16	0.000s
->SL	2	0.030s	400	5.722	0.005s	0.006s	0.175s	11.440	[98 1 1]%
->SR	1	0.035s	400	5.736	1.651s	0.338s	0.174s	5.737	[3 1 96]%
SL2	1	0.040s	400	5.731	0.000s	0.000s	0.058s	17.232	[100 0 0]%
SM	1	0.055s	800	11.451	0.000s	0.003s	0.063s	15.786	[97 3 0]%
SR2	1	0.045s	400	5.746	0.000s	0.000s	0.063s	15.821	[100 0 0]%
wshape{16,2,2,1,1,1}	800	50.923s	15.710
C	16	0.000s
->SL	2	0.030s	400	7.882	0.521s	0.248s	0.252s	7.931	[2 1 97]%
->SR	2	0.035s	400	7.855	0.412s	0.242s	0.252s	7.942	[4 1 95]%
SL2	1	0.040s	400	7.898	0.000s	0.000s	0.058s	17.164	[100 0 0]%
SM	1	0.055s	800	15.743	0.000s	0.081s	0.063s	15.821	[2 70 28]%
SR2	1	0.045s	400	7.872	0.000s	0.000s	0.063s	15.910	[100 0 0]%
wshape{16,2,2,1,1,2}	800	51.029s	15.677
C	16	0.000s
->SL	2	0.030s	400	7.839	0.506s	0.243s	0.253s	7.920	[4 2 94]%
->SR	2	0.035s	400	7.865	0.402s	0.247s	0.252s	7.927	[3 1 96]%
SL2	1	0.040s	400	7.855	0.000s	0.000s	0.058s	17.242	[100 0 0]%
SM	1	0.055s	800	15.691	0.000s	0.081s	0.063s	15.809	[2 69 29]%
SR2	2	0.045s	400	7.882	0.000s	0.000s	0.063s	31.757	[100 0 0]%
wshape{16,2,2,1,2,1}	800	35.432s	22.579
C	16	0.000s
->SL	2	0.030s	400	11.289	0.069s	0.079s	0.169s	11.817	[52 7 41]%
->SR	2	0.035s	400	11.342	0.605s	0.165s	0.174s	11.520	[6 1 93]%
SL2	1	0.040s	400	11.323	0.000s	0.000s	0.058s	17.283	[100 0 0]%
SM	2	0.055s	800	22.607	0.000s	0.000s	0.063s	31.710	[99 1 0]%
SR2	1	0.045s	400	11.376	0.000s	0.000s	0.063s	15.960	[100 0 0]%
wshape{16,2,2,1,2,2}	800	35.146s	22.762
C	16	0.000s
->SL	2	0.030s	400	11.381	0.279s	0.167s	0.173s	11.546	[4 1 95]%
->SR	2	0.035s	400	11.437	0.355s	0.172s	0.174s	11.488	[2 0 98]%
SL2	1	0.040s	400	11.416	0.000s	0.002s	0.058s	17.195	[97 3 0]%
SM	2	0.055s	800	22.792	0.000s	0.001s	0.063s	31.642	[98 2 0]%
SR2	2	0.045s	400	11.473	0.000s	0.000s	0.063s	31.702	[100 0 0]%
wshape{16,2,2,2,1,1}	800	51.058s	15.668
C	16	0.000s
->SL	2	0.030s	400	7.861	0.507s	0.248s	0.253s	7.915	[2 2 96]%
->SR	2	0.035s	400	7.834	0.422s	0.247s	0.252s	7.930	[3 1 96]%
SL2	2	0.040s	400	7.877	0.000s	0.000s	0.058s	34.555	[100 0 0]%
SM	1	0.055s	800	15.701	0.000s	0.081s	0.063s	15.803	[2 69 29]%
SR2	1	0.045s	400	7.851	0.000s	0.000s	0.063s	15.915	[100 0 0]%
wshape{16,2,2,2,1,2}	800	50.796s	15.749
C	16	0.000s
->SL	2	0.030s	400	7.902	0.531s	0.250s	0.252s	7.926	[1 1 98]%
->SR	2	0.035s	400	7.875	0.397s	0.246s	0.252s	7.943	[2 2 96]%
SL2	2	0.040s	400	7.928	0.000s	0.000s	0.057s	34.803	[100 0 0]%
SM	1	0.055s	800	15.782	0.000s	0.081s	0.063s	15.818	[1 70 29]%
SR2	2	0.045s	400	7.891	0.000s	0.000s	0.063s	31.826	[100 0 0]%
wshape{16,2,2,2,2,1}	800	35.107s	22.788
C	16	0.000s
->SL	2	0.030s	400	11.451	0.088s	0.090s	0.170s	11.795	[45 8 47]%
->SR	2	0.035s	400	11.394	0.602s	0.170s	0.174s	11.520	[3 1 96]%
SL2	2	0.040s	400	11.485	0.000s	0.000s	0.058s	34.738	[100 0 0]%
SM	2	0.055s	800	22.858	0.000s	0.001s	0.063s	31.626	[98 2 0]%
SR2	1	0.045s	400	11.429	0.000s	0.000s	0.063s	15.948	[100 0 0]%
wshape{16,2,2,2,2,2}	800	35.258s	22.690
C	16	0.000s
->SL	2	0.030s	400	11.345	0.314s	0.164s	0.174s	11.520	[7 0 93]%
->SR	2	0.035s	400	11.401	0.318s	0.171s	0.174s	11.519	[3 1 96]%
SL2	2	0.040s	400	11.379	0.000s	0.000s	0.058s	34.437	[100 0 0]%
SM	2	0.055s	800	22.718	0.000s	0.002s	0.063s	31.633	[97 0 3]%
SR2	2	0.045s	400	11.435	0.000s	0.000s	0.063s	31.826	[100 0 0]%
wshape{16,2,3,1,2,1}	800	34.612s	23.114
C	16	0.000s
->SL	2	0.030s	400	11.557	0.688s	0.167s	0.171s	11.722	[3 1 96]%
->SR	3	0.035s	400	11.615	0.004s	0.006s	0.175s	17.099	[96 1 3]%
SL2	1	0.040s	400	11.598	0.000s	0.000s	0.058s	17.198	[100 0 0]%
SM	2	0.055s	800	23.153	0.000s	0.001s	0.063s	31.580	[98 2 0]%
SR2	1	0.045s	400	11.656	0.000s	0.001s	0.063s	15.860	[98 2 0]%
wshape{16,3,2,1,2,1}	800	35.735s	22.387
C	16	0.000s
->SL	3	0.030s	400	11.248	0.004s	0.005s	0.171s	17.592	[97 1 2]%
->SR	2	0.035s	400	11.194	0.734s	0.168s	0.174s	11.488	[6 0 94]%
SL2	1	0.040s	400	11.281	0.000s	0.001s	0.058s	17.216	[99 1 0]%
SM	2	0.055s	800	22.454	0.000s	0.001s	0.063s	31.642	[99 1 0]%
SR2	1	0.045s	400	11.227	0.000s	0.000s	0.063s	15.887	[100 0 0]%
wshape{16,3,3,1,2,1}	800	25.908s	30.879
C	16	0.000s
->SL	3	0.030s	400	15.554	0.164s	0.121s	0.190s	15.830	[6 1 93]%
->SR	3	0.035s	400	15.440	0.186s	0.124s	0.190s	15.803	[4 1 95]%
SL2	1	0.040s	400	15.616	0.000s	0.002s	0.058s	17.206	[98 2 0]%
SM	2	0.055s	800	31.025	0.000s	0.016s	0.063s	31.640	[51 48 1]%
SR2	1	0.045s	400	15.503	0.000s	0.003s	0.063s	15.919	[95 5 0]%
#651.876s


7.6. Конфигурация other

Это место для вашего кода. Смелее! Создавайте любую конфигурацию и смотрите на результат.

А пока там орудует банда серверов из начала статьи:

И вот что у них получается:

$ demoscale other
#CmdName			NumMsg	Time	RPS
other{8,2,2,2,2,2,2,2,1}	16	8.942s	1.789
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	2	0.030s	16	1.789	1.389s	0.972s	1.111s	1.800	[13 1 86]%
SL	2	0.050s	16	1.912	0.000s	0.000s	0.533s	3.754	[100 0 0]%
SR	2	0.055s	16	1.905	0.000s	0.000s	0.530s	3.775	[100 0 0]%
SL1	2	0.070s	16	1.982	0.000s	0.000s	0.229s	8.717	[100 0 0]%
SL2	2	0.075s	16	1.971	0.000s	0.000s	0.239s	8.357	[100 0 0]%
SR1	2	0.080s	16	1.979	0.000s	0.000s	0.210s	9.516	[100 0 0]%
SR2	2	0.085s	16	1.969	0.000s	0.000s	0.239s	8.369	[100 0 0]%
DB	1	0.100s	64	7.329	0.000s	0.016s	0.117s	8.564	[89 11 0]%
$ demoscale++ other
#CmdName			NumMsg	Time	RPS
other{8,2,2,2,2,2,2,2,1}	16	8.871s	1.804
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	8	0.000s
->S	2	0.030s	16	1.804	1.378s	0.901s	1.101s	1.817	[13 12 75]%
SL	2	0.050s	16	1.933	0.000s	0.000s	0.516s	3.873	[100 0 0]%
SR	2	0.055s	16	1.925	0.000s	0.000s	0.537s	3.728	[100 0 0]%
SL1	2	0.070s	16	2.009	0.000s	0.000s	0.208s	9.624	[100 0 0]%
SL2	2	0.075s	16	1.997	0.000s	0.000s	0.246s	8.114	[100 0 0]%
SR1	2	0.080s	16	2.005	0.000s	0.000s	0.229s	8.718	[100 0 0]%
SR2	2	0.085s	16	1.993	0.000s	0.000s	0.244s	8.197	[100 0 0]%
DB	1	0.100s	64	7.383	0.000s	0.019s	0.119s	8.375	[86 14 0]%


8. Заключение

Горизонтально масштабируемые серверы определяются архитектурой, которая РАЗРЕШАЕТ параллельное исполнение инстансов.

Вы ничего не сделаете с плохой Архитектурой. Вы ничего не измените в Большой Организации. Ничего существенного.

А по мелочи, вы теперь знаете:

  1. Чем горизонтальное масштабирование отличается от вертикального.
  2. Почему не надо слепо масштабироваться.
  3. Почему нет смысла ускоряться, где не надо. Т.е. почти везде.
  4. Нами были исследованы все базовые конфигурации и обнаружены удивительные результаты.
  5. Шарик проставился пивом.
Как это все применить?

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

Как показала практика, в реальном Мире очень сильно запущены 4 случая из 5. Это поле непаханое, но... в чужом Деле вам ничего не дадут изменить.

ОКай, допустим мы правильно видим реальные факты и можем оптимально масштабировать. Что дальше?

А дальше только Полная Автоматизация!

Но это Сказки братьев Брин.


9. P.S. Программа demoscale++

Ну что, продолжим?

Прежде всего, всем огромное спасибо за фидбек! Я получил неожиданно много вопросов и большинство дискуссий, так или иначе, упоминали C++. Самый частый вопрос: А что получится на C++?

На C++ получится demoscale++. Это же очевидно!

Т.е. все то же самое, только быстрее. И предсказуемее... Скучно, товарищи! Если не пишете на C++. А если пишете?

Никогда не задумывались, как бы выглядела та же самая Go программа на C++? А я вдруг задумался и теперь знаю ответ.

Но все-таки вернемся к сути дела. На примере тяжелой конфигурации

и обеих программ:

$ demoscale wshape 800 C/16/0 SL/3/30 SR/3/35 SL2/1/40 SM/2/55 SR2/1/45
#CmdName		NumMsg	Time	RPS
wshape{16,3,3,1,2,1}	800	32.127s	24.901
#Name	NumInst	Slp	NumMsg	RPS	Wait1	Wait2	Work	RPS(W)	[E NE F]
C	16	0.000s
->SL	3	0.030s	400	12.530	0.277s	0.143s	0.235s	12.770	[9 3 88]%
->SR	3	0.035s	400	12.451	0.116s	0.151s	0.236s	12.712	[5 2 93]%
SL2	1	0.040s	400	12.578	0.000s	0.004s	0.064s	15.617	[95 5 0]%
SM	2	0.055s	800	25.000	0.000s	0.025s	0.078s	25.493	[48 40 12]%
SR2	1	0.045s	400	12.500	0.000s	0.004s	0.064s	15.510	[95 5 0]%
$ demoscale++ wshape 800 C/16/0 SL/3/30 SR/3/35 SL2/1/40 SM/2/55 SR2/1/45
wshape{16,3,3,1,2,1}	800	25.908s	30.879
C	16	0.000s
->SL	3	0.030s	400	15.554	0.164s	0.121s	0.190s	15.830	[6 1 93]%
->SR	3	0.035s	400	15.440	0.186s	0.124s	0.190s	15.803	[4 1 95]%
SL2	1	0.040s	400	15.616	0.000s	0.002s	0.058s	17.206	[98 2 0]%
SM	2	0.055s	800	31.025	0.000s	0.016s	0.063s	31.640	[51 48 1]%
SR2	1	0.045s	400	15.503	0.000s	0.003s	0.063s	15.919	[95 5 0]%

Ага, 25.908 секунды вместо 32.127 -- это 81%. Т.е. demoscale++ процентов на 20 быстрее.

Но!

Вы же там не забыли про время задержки?

Например, если время работы SL 0.235 секунды, то общее время задержки SL, SL2 и SM будет 0.125 секунды (=30+40+55). Короче, все отнять и поделить: (190-125)/(235-125)=59%.

О! 40% -- это уже не пиво. Надо сказать Шарику.

А без шуток, скучный результат прекрасен! Все рассмотренные варианты масштабирования полностью верны для demoscale++ и дают аналогичный выхлоп. Test PASSED.


10. P.P.S. Bandwidth. Throughput. Latency.

Вы же еще не ушли? Айда на конвейере кататься!

Еще один частый вопрос читателей -- смысл терминов bandwidth, throughput и latency. Тут вроде как все понятно, но порой возникает и путаница. Даже чаще!

Так и быть, открою Профессиональный Секрет.

Если не очень понятно английское слово, ваш первый шаг -- хороший online-словарь! Например Google Translate:

Ага! То есть, ой...

Чуете вой над болотом? Это Шарик услышал про Google Translate.

Срочно спасаем друга:

Спасибо, а где кататься?

Окай, представьте душевную ленту конвейера суровой Советской Фабрики! Здесь latency -- это время, за которое деталь проезжает от начала до конца конвейера. А throughput -- это количество деталей, проходящих мимо Шарика за секунду.

Тогда:

Что выберет Шарик? Вопросы, вопросы...
Copyright © С. Деревяго, 2024

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