Як мы распрацавалі Swarm, нашу ўнутраную рамочную структуру для тэставання нагрузкі, каб справіцца з задавальненнем тэставання ад канца да канца, патэнцыйных пратаколаў і шырокіх сімуляцый ботаў у gamedev.
Як мы распрацавалі Swarm, нашу ўнутраную рамочную структуру для тэставання нагрузкі, каб справіцца з задавальненнем тэставання ад канца да канца, патэнцыйных пратаколаў і шырокіх сімуляцый ботаў у gamedev.
У цяперашні час, гэта вельмі цяжка падумаць пра праграмнае забеспячэнне безнекаторыяУсё, што вы чулі пра карысць пара раней - усяго толькі чуткі і павер'і.
Гэтак будуць апошнія першымі, і першыя - апошнімі, бо шмат пакліканых, але мала выбраных".
Узнагароджанне на нашу архітэктуру
Без ідучы ў любыя небяспечныя дэталі, наш бак-энд складаецца з набору класічных паслуг і кластера сумяшчальных вузлаў, дзе жывуць акцёры.
Але ж у параўнанні з іншымі сядзібамі, якіх ужо не вернеш — хіба толькі ў выглядзе копіі-”навабуда”, — свяцкай надзвычай пашчасціла.ГэтаНу, мы маем тое ж самае сказаць пра GDD (Document Game Design).
Фактычна, прысвечаныя такому спорту як бокс, онлайн гульні заўсёды прызнаваліся нашмат больш цікавымі чым звычайныя аднакарыстальніцкія цацкі.
У нас функцыянуюць таварыствы “Разумнікі і разумніцы”, “Даследчык”, а таксама адзіная ў Магілёўскай вобласці астранамічная пляцоўка.
Выпрабаванне
Як вы маглі падумаць, адзінкавы тэст для акцёраў і паслуг (і нагрузка / інтэграцыя тэст для паслуг) не адрозніваецца ад таго, што вы знойдзеце ў любым іншым тыпу праграмнага забеспячэння.
- Загрузка і інтэграцыя теста акцёраў
- Вынікі пошуку - download and load backend
Мы не будзем глядзець на тэставанне акцёраў падрабязна тут, таму што гэта не спецыяльна для GameDev, але адносіцца да самай мадэлі акцёра. звычайны падыход з'яўляецца мець спецыяльны адзін-вунд кластер, які падыходзіць, каб быць з'яўлены ў памяці ўнутры адным тэставанні і што таксама можа вытрымаць усе выхадныя патрабаванні і называць акцёраў API. Мы выкарыстоўваем той жа падыход.
Гэта сказана, што рэчы пачынаюць стаць больш цікавымі, калі мы прыходзім да загрузкі або канца да канца тэставання - і гэта тое, дзе наша гісторыя пачынаецца.
Так, у нашым выпадку, кліент-прыкладанне - самая гульня.Гісторыя выкарыстоўвае Unreal Engine, так што код напісаны на C++, а на серверы мы выкарыстоўваем C#.Плаеры ўзаемадзейнічаюць з элементамі UI ў гульні, вырабляючы патрабаванні ў бакэнд (або патрабаванні зроблены непасрэдна).
На гэты момант, вялікая частка фрэймаў проста перастаюць працаваць для нас, і любыя віды селену, якія лічаць кліент прыкладанняў браўзэра, выйшлі з галіны.
І тут мы павінны слухаць саміх людзей — інакш кажучы, тое, што яны кажуць, для мяне самае важнае.
- Камунікацыя адбываецца праз WebSocket злучэнне
- Гэта схема-першае; мы выкарыстоўваем Protobuf, каб вызначыць структуру паведамлення і паслуг
- WebSocket паведамлення з'яўляюцца Protobuf паведамлення ўпакоены ў кантэйнер з метаданнымі, якія імітуюць некаторыя неабходныя gRPC-адукацыйныя інфармацыі, такія як URL і заголоўкі
Так што любы інструмент, які не дазваляе вызначаць наладныя пратаколы, не падыходзіць для задачы.
Загрузіць інструмент, каб выпрабаваць іх усе
Усё ж казаць краіне «Добрай раніцы» для мяне больш проста і зразумела, чым улюбляцца і дэманстраваць сімпатыю на камеру.
У кожнага з іх былі свае плюсы і мінусы, але было некалькі рэчаў, якія ніхто з іх не мог вырашыць без вялікіх (іначай унутраных) зменаў:
- У першай частцы сустрэчы школьнікі пазнаёміліся інфармацыйнымі блокамі.
- Іншая справа, як раскрыць душу героя... Калі мы кажам пра кіно, каб захаваць гэтую натуральнасць паўсядзённасці і выявіць драму, неабходны моцны сцэнарый.
- Найбольш традыцыйным з'яўляецца выкарыстанне назвы «Insecta» (Уільям Элфард Ліч, 1815) — у значэнні шасціногія; але і ў сучаснай навуковай літаратуры назву «Insecta» ужываюць не менш чым у трох ці чатырох розных значэннях[9].
— Наша фірма існуе ўжо сёмы год, мы спецыялізуемся на будаўнічых работах.СваркаБыў народжаны
Сварка
На высокім узроўні, задачай Swarm з'яўляецца запуск шмат акцёраў, якія будуць вырабляць нагрузку на сервер. Гэтыя акцёры называюцца ботамі, і яны могуць імітаваць паводзіны кліента або прысвечаны паводзіны сервера.
Больш формальна, тут ёсць спіс патрабаванняў для інструмента:
- Здаровая касметыка вы можаце зрабіць самі
- Конкуренцыя не можа быць праблемай
- Код можа быць аўтаматычным
- Комунікацыя з ботам да бота павінна быць лёгкай
- Многія інстанцыі павінны быць злучаемымі для стварэння дастатковай нагрузкі
- Інструмент павінен быць лёгкім і здольным ствараць достойны напружанне на самай бакэйн
Як бонус, я таксама дадаў некалькі дадатковых пунктаў:
- У будучыні гэта можа стаць выдатным і паспяховым бізнесам.
- Гэта могуць быць лясныя сцежкі, участкі грэйдарных і пяшчаных дарог.
- Гэтая знаходка атрымала назву «Хлопчык з Турканы»[4].
- Боты павінны быць ў стане існаваць адрознівальна ад тэставага інструмента, а гэта значыць, што не павінна быць жорсткіх залежнасцяў
Наша мета
Давайце падумаем, які код мы хацелі б напісаць; мы будзем разглядаць бот як лялька, і ён не можа рабіць рэчы самому, ён можа толькі падтрымліваць інварыяльныя, у той час як Сцэнарый - гэта лялька, якая выцягвае струны.
public class ExampleScenario : ScenarioBase
{
/* ... */
public override async Task Run(ISwarmAgent swarm)
{
// spawn bots and connect to backend
var leader = SpawnClient();
var follower = SpawnClient();
await leader.Login();
await follower.Login();
// expect incoming InviteAddedEvent
var followerWaitingForInvite = follower.Group.Subscription
.ListenOnceUntil(GroupInviteAdded)
.ThrowIfTimeout();
// leader sends invite and followers waits for it
await leader.Group.SendGroupInvite(follower.PlayerId);
await followerWaitingForInvite;
Assert.That(follower.Group.State.IncomingInvites.Count, Is.EqualTo(1));
var invite = follower.Group.State.IncomingInvites[0];
// now vice versa, the leader waits for an event...
var leaderWaitingForAccept = leader.Group.Subscription
.ListenOnceUntil(InviteAcceptedBy(follower.PlayerId))
.ThrowIfTimeout();
// ... and follower accept invite, thus producing the event
await follower.Group.AcceptGroupInvite(invite.Id);
await leaderWaitingForAccept;
Assert.That(follower.Group.State.GroupId, Is.EqualTo(leader.Group.State.GroupId));
PlayerId[] expectedPlayers = [leader.PlayerId, follower.PlayerId];
Assert.That(leader.Group.State.Players, Is.EquivalentTo(expectedPlayers));
Assert.That(follower.Group.State.Players, Is.EquivalentTo(expectedPlayers));
}
}
Стэінг
У далейшым Андрэй працягнуў пісаць песні для Лявонцьева, а таксама многіх іншых артыстаў.GroupInviteAddedEvent
Бот можа як адказваць на гэтыя падзеі ўнутры, так і даць магчымасць назіраць на іх з замежнага кода.
public Task SubscribeToGroupState()
{
Subscription.Listen(OnGroupStateUpdate);
Subscription.Start(api.GroupServiceClient.SubscribeToGroupStateUpdates, new SubscribeToGroupStateUpdatesRequest());
return Task.CompletedTask;
}
У той час як код вельмі просты (і, як вы можаце гаварыць,OnGroupStateUpdate
У гэтым выпадку пешаходы, якія сканчаюць пераход, уяўляюць істотную небяспеку (мал.
StreamSubscription
Гэта сама аIObservable<ObservedEvent<TStreamMessage>>
І гэта дае карысныя пашырэння, мае залежны жыццёвы цикл, і пакрыты метрыкі.
Інакш проста не магло быць, бо калі ўжо стварылі такі папулярны мультфільм пра гонкі, а гонкі, як вядома, з'яўляюцца самым папулярным гульнявым жанрам.
case GroupUpdateEventType.GroupInviteAddedEvent:
State.IncomingInvites.Add(ev.GroupInviteAddedEvent.GroupId, ev.GroupInviteAddedEvent.Invite);
break;
Конкуренцыя не можа быць праблемай
Ідэя простая: ніколі не выконваць код, які сумесна належыць сцэнарыю або боту, які нарадзіўся ў гэтым сцэнарыі, і, да таго ж, ўнутраны код бота / модуля не павінна быць выконваецца сумесна з іншымі часткамі бота.
Гэта падобна на чытанне / напісанне замка, дзе сцэнарны код прачытаецца (даступ у спісе) і бот-код напісанне (эксклюзіўны доступ).
Задачы і контекст синхронізацыі
Два механізмы, згаданыя ў гэтай назве, цяпер з'яўляюцца вельмі магутнымі часткамі асанкцыйнага кода ў C#. Калі вы ніколі распрацавалі прыкладанне WPF, вы, вядома, ведаеце, што гэта асанкцыя.DispatcherSynchronizationContext
Гэта выдатная платформа для выказвання маладымі людзьмі сваіх пазіцый, іх абмеркавання.
У нашым сцэнары мы не цікавімся афіннасцю шклоў, а затым больш цікавімся парадкам выканання задач у сцэнары.
Перш чым напісаць код нізкага ўзроўню, давайце паглядзім на адзін не вельмі вядомы клас, які называеццаConcurrentExclusiveSchedulerPair
З тых часоў
Размова сапраўды вельмі важная — мы ж усе цудоўна разумеем, што любы, самы лепшы дэкрэт можна звесці на нішто практыкай прымянення.
Размова сапраўды вельмі важная — мы ж усе цудоўна разумеем, што любы, самы лепшы дэкрэт можна звесці на нішто практыкай прымянення.
Гэта выглядае, як мы хочам! Цяпер мы павінны гарантаваць, што ўвесь код ў сцэнары выконваецца наConcurrentScheduler
У той час як код бота выконваецца наExclusiveScheduler
.
Узнікае пытанне: калі іх ведае увесь свет, ці можна назваць іх няўдачнікамі?
public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch)
{
return Task.Factory.StartNew(
() =>
{
/* <some observability and context preparations> */
return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch);
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap();
}
ІRunScenarioInstance
Наступным крокам з'яўляецца выбар адпаведнага абсталявання дляSetUp
іRun
Наступная Артыкулы пра калектыўныя работы, якія праводзяцца ў калектыве (ScenarioScheduler
Гэта конкурсная часткаConcurrentExclusiveSchedulerPair
)
Цяпер, калі мы знаходзімся ў сцэнарным кодзе і мы робім гэта...
public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch)
{
return Task.Factory.StartNew(
() =>
{
/* <some observability and context preparations> */
return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch);
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap();
}
...асінк-статусная машына робіць сваю работу для нас, захоўваючы графік для нашых задач.
ЦяперSendGroupInvite
Працуе на параўнальным графіку таксама, так што мы робім той жа трюк для яго, таксама:
public Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId)
{
return Runtime.Do(async () =>
{
var result = await api.GroupServiceClient.SendGroupInviteAsync(/* ... */);
if (!result.HasError)
{
State.OutgoingInvites.Add(inviteePlayerId);
}
return result;
});
}
У гэтым выпадку пешаходы, якія сканчаюць пераход, уяўляюць істотную небяспеку (мал.Task.Factory.StartNew
З правільным графікам.
Код генерацыі
Цяпер нам трэба рабіць усё, што ў нас ёсць.Do
і, хоць гэта вырашае праблему, гэта схільна да няправільнасці, лёгка забыцца, і, у асноўным, гэта выглядае дзіўна.
Давайце паглядзім на гэтую частку кода:
await leader.Group.SendGroupInvite(follower.PlayerId);
І тут у нас ёсць ботGroup
Існаванне І.Group
— гэта модуль, які існуе толькі для падзялення коду на асобныя класы і для таго, каб не наблытацьBot
І клас.
public class BotClient : BotClientBase
{
public GroupBotModule Group { get; }
/* ... */
}
Давайце скажам, што модуль павінен мець інтэрфейс:
public class BotClient : BotClientBase
{
public IGroupBotModule Group { get; }
/* ... */
}
public interface IGroupBotModule : IBotModule, IAsyncDisposable
{
GroupBotState State { get; }
Task<SendGroupInviteResponse> SendGroupInvite(PlayerId toPlayerId);
/* ... */
}
public class GroupBotModule :
BotClientModuleBase<GroupBotModule>,
IGroupBotModule
{
/* ... */
public async Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId)
{
// no wrapping with `Runtime.Do` here
var result = await api.GroupServiceClient.SendGroupInviteAsync(new() /* ... */);
if (!result.HasError)
{
State.OutgoingInvites.Add(new GroupBotState.Invite(inviteePlayerId, /* ... */));
}
return result;
}
|
Ніякіх падставаў для грамадскіх хваляванняў ці рэнесансу ісламскіх радыкалаў, няма.
Чароўная печка прапануе Вам навагоднія частункі. - Ці ведаеце вы?Runtime.Do
Запішыце ў:
public const string MethodProxy =
@"
public async $return_type$ $method_name$($method_params$)
{
$return$await runtime.Do(() => implementation.$method_name$($method_args$));
}
";
Інструменты
Наступным крокам з'яўляецца інструментаванне кода, каб сабраць метрыкі і сляды. Па-першае, кожны выклик API трэба назіраць. Гэтая частка вельмі прастая, так як наш транспарт эфектыўна робіць выгляд, што гэта канал gRPC, так што мы толькі выкарыстоўваем правільна напісаны інтэрцэптар...
callInvoker = channel
.Intercept(new PerformanceInterceptor());
ДзеCallInvoker
Гэта абстрактная абстракцыя RPC-выкліку з боку кліента.
І тут мы павінны слухаць саміх людзей — інакш кажучы, тое, што яны кажуць, для мяне самае важнае.IInstrumentationFactory
З наступным інтэрфейсам:
public interface IInstrumentationFactory
{
public IInstrumentationScope CreateScope(
string? name = null,
IInstrumentationContext? actor = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0);
}
Цяпер вы можаце пакрыць часткі, якія вас цікавяць:
public Task AcceptGroupInvite(GroupInvideId inviteId)
{
using (instrumentationFactory.CreateScope())
{
var result = await api.GroupServiceClient.AcceptGroupInviteAsync(request);
}
}
Калі вы таксама хочаце прывабіць удачу на свой лад, бярыце прыклад з герояў нашага матэрыяла!
public const string MethodProxy =
@"
public async $return_type$ $method_name$($method_params$)
{
using(instrumentationFactory.CreateScope(/* related args> */))
{
$return$await runtime.Do(() => implementation.$method_name$($method_args$));
}
}
";
Інструментальны аб'ём запісвае час выканання, выключэнні, стварае распаўсюджаныя трасы, ён можа напісаць логі дэбаг, і ён робіць шмат іншых канфігураваных рэчаў.
Сцягнуць Bot-to-Bot
Прыклад у секцыі «Наша мэта» не паказвае ніякай інфармацыі. Мы толькі ведаем бот следчыPlayerId
Калі рабочы пакідаў наймальніка без уважлівых прычын да заканчэння тэрміну найму - гэта прызнавалася самавольным адыходам.
Я першапачаткова планаваў рэалізаваць невялікі шаблон чорнай табліцы з распаўсюджанай памяшканнем ключавых значэнняў для сувязі (такім як Redis), але пасля выконвання некаторых доказу канцэпцыйных тэстаў, выявілася, што аб'ём працы можа быць значна зменшыны да двух больш простых канцэпцый: чагосьці і селектара.
Лістапад — билеты
У рэальным свеце, гульцы сустракаюцца з усім інакш – яны пішуць прамыя паведамленні або выкарыстоўваюць галасовы чат. З ботамі, нам не трэба імітаваць гэтую гранулярнасць, і нам проста трэба прапанаваць наміры.ХтосьціПаглядзі на мяне, калі ласка, у групу».І вось, дзе ўвайшлі ў гульні білеты:
public interface ISwarmTickets
{
Task PlaceTicket(SwarmTicketBase ticket);
Task<SwarmTicketBase?> TryGetTicket(Type ticketType, TimeSpan timeout);
}
Бот можа ставіць квіток, а затым іншы бот можа атрымаць гэты квіток.ISwarmTickets
Гэта нічога іншага, як папярэдні працэсticketType
(На самай справе гэтаЛегкаБольш за тое, таму што ёсць дадатковыя варыянты, каб пазбегнуць ботаў ад атрымання сваіх уласцівак, а таксама іншых невялікіх нараджанняў).
З дапамогай гэтага інтэрфейсу мы можам наколькі раздзяліць прыкладны сцэнарый на два незалежныя сцэнары. (тут усё дадатковы код знішчаецца, каб ілюстраваць асноўную ідэю):
private async Task RunLeaderRole(ISwarmAgent swarm)
{
var ticket = await swarm.Blackboard.Tickets
.TryGetTicket<BotWantsGroupTicket>(TimeSpan.FromSeconds(5))
.ThrowIfTicketIsNull();
await bot.Group.SendGroupInvite(ticket.Owner);
await bot.Group.Subscription.ListenOnceUntil(
GotInviteAcceptedEvent,
TimeSpan.FromSeconds(5))
.ThrowIfTimeout();
}
private async Task RunFollowerRole(ISwarmAgent swarm)
{
var waitingForInvite = bot.Group.Subscription.ListenOnceUntil(
GotInviteAddedEvent,
TimeSpan.FromSeconds(5))
.ThrowIfTimeout();
await swarm.Blackboard.Tickets.PlaceTicket(new BotWantsGroupTicket(bot.PlayerId));
await waitingForInvite;
await bot.Group.AcceptGroupInvite(bot.Group.State.IncomingInvites[0].Id);
}
Селектар
Мы маем два розныя паводзіны, адзін для лідара, адзін для следчыка. Разумеецца, яны могуць быць падзеленыя на два розныя сцэнары і запушчаны паралельна.
public override async Task Run(ISwarmAgent swarm)
{
var roleAction = await swarm.Blackboard
.RoundRobinRole(
"leader or follower",
Enumerable.Repeat(RunFollowerRole, config.GroupSize - 1).Union([RunLeaderRole]));
await roleAction(swarm);
}
І тут,RoundRobinRole
Гэта проста фантастычны ўпрыгожванне вакол сумеснага літара і аперацыя модуля, каб выбраць правільны элемент з спісу.
Класіфікацыя Swarms
Цяпер, з усімі сувязямі, якія захоўваюцца за часам і распаўсюджанымі лічбамі, становіцца трымавіяльным увядзенне або вузла оркестратара, або выкарыстанне некаторых існуючых MQ і KV памяшканняў.
Цікавы факт: мы ніколі не скончылі рэалізацыю гэтай функцыі.Калі QA атрымаў свае рукі на адным вузле рэалізацыі SwarmAgent
Але іх выступленні гучалі так, быццам бы яны доўга рэпеціравалі з аркестрам.
Еўрапейская перспектыва
— Вераніка Уладзіміраўна, што гэта за 11 андрагагічных умоў, пры якіх дарослыя без цяжкасцей могуць засвоіць новыя тэхналогіі ў фізічнай культуры?
Сцягнуць бенчмарк:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4651/22H2/2022Update)
Intel Core i7-10875H працэсар 2.30GHz, 1 працэсар, 16 логічных і 8 фізічных ядраў
Загрузіць .NET SDK 9.0.203
[Хост] : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX2
.NET 9.0 : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX2
private async Task SimpleWorker()
{
var tasks = new List<Task>();
for (var i = 0; i < Parallelism; ++i)
{
var index = i;
tasks.Add(Task.Run(async () =>
{
for (var j = 0; j < Iterations; ++j)
{
await LoadMethod(index);
}
}));
}
await Task.WhenAll(tasks);
}
private async Task SchedulerWorker()
{
// scenarios are prepared in GlobalSetup
await Task.WhenAll(scenarios.Select(LaunchScenarioAsyncThread));
}
public class TestScenario : ScenarioBase
{
public override async Task Run()
{
for (var iteration = 0; iteration < iterations; ++iteration)
{
await botClient.Do(BotLoadMethod);
}
}
}
Перш за ўсё, давайце паглядзім на амаль чыстыя перавагі планавання супраць простых заданняў.
У гэтым выпадку, оба метады загрузкі з'яўляюцца наступнымі:
private ValueTask CounterLoadMethod(int i)
{
Interlocked.Increment(ref StaticControl.Counter);
return ValueTask.CompletedTask;
}
Вынікі для ітэрацый = 10:
WorkerType |
Parallelism |
Mean |
Error |
StdDev |
Gen0 |
Gen1 |
Gen2 |
Allocated |
---|---|---|---|---|---|---|---|---|
Simple |
10 |
3.565us |
0.0450us |
0.0421us |
0.3433 |
- |
- |
2.83KB |
Simple |
100 |
30.281us |
0.2720us |
0.2544us |
3.1128 |
0.061 |
- |
25.67KB |
Simple |
1000 |
250.693us |
2.1626us |
2.4037us |
30.2734 |
5.8594 |
- |
250.67KB |
Scheduler |
10 |
40.629us |
0.7842us |
0.8054us |
8.1787 |
0.1221 |
- |
66.15KB |
Scheduler |
100 |
325.386us |
2.3414us |
2.1901us |
81.0547 |
14.6484 |
- |
662.09KB |
Scheduler |
1000 |
4,685.812us |
24.7917us |
21.9772us |
812.5 |
375 |
- |
6617.59KB |
Простыя
10
3 565 працэнтаў
0,450 млн
0,421 працэнта
0.3433
-
-
2.8 Кб
Простыя
100
30281 Вынікі
2720 г.
0,2544 г.
3.1128
0.061
-
257 Кб
Простыя
1000
250 693
1626 г.
2.4037Сірыя
30.2734
5.8594
-
250.67 Кб
Графік
10
40 629 працэнтаў
1 742
0 805
8.1787
0.1221
-
66.15 Кб
Графік
100
325.386Адміністрацыя
2.3414Адміністрацыя
2 1901 год
81.0547
14.6484
-
62.09 Кб
Графік
1000
4 685 812
247917Правіць
21 772
812.5
375
-
6617.59Кб
Здавалася б «сумнае» пытаннне разглядалася, а так лягло на душу! — так лягло на душу! — так лягло на душу!
Што можа быць рэальным найгіршым сцэнарыстычным сцэнарам? Ну, а што пра функцыю, якая не робіць нічога больш, чым выклик API? З аднаго боку, амаль кожны спосаб бота робіць гэта, але з іншага боку, калі няма нічога больш, чым толькі выклікі API, то ўсе спробы сінхранізацыі згубленыя, ці не так?
Вынікі пошуку - only callPingAsync
Для папросту, кліенты RPC захоўваюцца з-за ботаў.
private async ValueTask PingLoadMethod(int i)
{
await clients[i].PingAsync(new PingRequest());
}
Вось вынікі, яшчэ 10 ітэрацый (сервер GRPC знаходзіцца ў локальной сеткі):
WorkerType |
Parallelism |
Mean |
Error |
StdDev |
Gen0 |
Gen1 |
Allocated |
---|---|---|---|---|---|---|---|
Simple |
100 |
94.45 ms |
1.804 ms |
2.148 ms |
600 |
200 |
6.14 MB |
Simple |
1000 |
596.69 ms |
15.592 ms |
45.730 ms |
9000 |
7000 |
76.77 MB |
Scheduler |
100 |
95.48 ms |
1.547 ms |
1.292 ms |
833.3333 |
333.3333 |
6.85 MB |
Scheduler |
1000 |
625.52 ms |
14.697 ms |
42.405 ms |
8000 |
7000 |
68.57 MB |
Простыя
100
94.45 МЗ
1804 г.
2148 МЗ
600
200
6.14 Мб
Простыя
1000
6956 мс
15 592 МЗ
45 730 мс
9000
7000
77.7 Мб
Графік
100
95,48 мс
1 547 МЗ
1 292 МЗ
833.3333
333.3333
685 Мб
Графік
1000
625.52 мс
14 697 працэнтаў
42 405 мс
8000
7000
68,57 Мб
Як і чакалася, ўплыў на працэдуру на рэальныя працы сцэнары небяспечны.
Аналіз
Але серыя онлайн цацак Assassins creed 3 і Assassins creed 4, а таксама астатнія часткі забавы таксама заслугоўваюць увагі і вывучэння.
Спадзяёмся, што матэрыяльная падтрымка будзе і з боку Міністэрства культуры, а таксама фонду краін СНД, цяпер складаем праграму, якую павязём у Маскву на ўзгадненне, будзем там адстойваць свае пазіцыі.
Як часта бывае ў такія хвіліны, усе разгубіліся.
Праўда, сляды патрэбныя для добрай назіральнасці. Для тэставых бягун Swarm, сляды кліента звязаны з заднімі слядамі на адным дрэве. Спецыфічныя сляды можна знайсці паconnectionIdD/botId
Гэта дапамагае ў дэбютаванні.
Заўвага: DevServer з'яўляецца зручным манолітным будынкам усё-на-адзіне бакэйн-сервісаў, якія будуць запушчаны на ПК распрацоўшчыка, і гэта спецыяльна распрацаваны прыклад, каб зменшыць колькасць дэталяў на экране.
З дапамогай існуючых інструментаў, гэтыя трасы могуць быць праглядзены і аналізаваны выкарыстоўваючы ўбудаваныя магчымасці, такія як PerfettoSQL.
Што здарылася ў нас у канцы
У нас цяпер ёсць SDK, на якім пабудаваны і GameClient і Dedicated Server боты. Гэтыя боты цяпер падтрымліваюць большую частку гульнявай логікі — яны ўключаюць падсістэмы сяброў, групы падтрымкі і matchmaking.
Арганізатар спеўных варштатаў у межах праекта «CHOICE-Беларусь: спадчына i сучаснасць» разважае, як можна пракласці мост паміж музеем і людзьмі.
Эксперты адзначаюць, што збор соку з бярозы зусім не шкодзіць дрэве, так як падчас гэтага працэс выдаляецца толькі 1% вадкасці.
Swarm дапамагае не толькі з тэстамі, але звычайна выкарыстоўваецца для рэпродукцыі і выпраўлення баг у гульнявай логіцы, асабліва калі гэта складана зрабіць ручна, напрыклад, калі вам патрэбны некалькі гульнявых кліентаў, якія выконваюць спецыяльную сцэну акцый.
У рэшце рэшт, мы надзвычай задаволеныя нашым вынікам і, безумоўна, не маем жалю пра цяжкасці, выдаткаванае на распрацоўку нашай собственнай сістэмы выпрабаванняў.
Я вельмі рада, што ён быў у маім жыцці, бо гэта ён, калі можна так сказаць, паставіў машыну майго мыслення.