Тестування навантаження: як генерувати запити за секунду?


14

У мене є серверний компонент, який працює над Zeroc-ICE. Коли я хотів завантажити тест, я подумав, що використання паралельної бібліотеки для створення декількох запитів це зробить. Але це закінчується таким чином. Використовувати бібліотеку Parallel (Parallel.For) від C #, мабуть, було простіше, але, здається, не було точно генерувати все паралельно в одну мить. Отже, це не може бути визначенням створення N запитів в секунду. Як мені це зробити? Я думаю, кожен, хто хоче спочатку зробити тестування навантаження, насправді подумає про це.

  1. Який ефективний спосіб насправді створити N запитів за секунду?

  2. Ще один міф - про паралельне програмування. Будь ласка, просвітліть нас, якщо ви використовували паралельні схеми програмування в C # або .Net взагалі. Уявіть, у мене є 5 процесів. Як запустити всі п’ять процесів одночасно. Що це означає для мого споживання ресурсів? Я спробував прочитати багато матеріалів, доступних через мережу, але я отримую все більше запитань, ніж відповіді на мої запитання.

  3. Я використовував Parallel.For і створив N потоків і виміряв час. Потім я спробував те ж саме, використовуючи Task.Factory.start для перерахування завдань. Виміряний час був різним. Отже, чим саме відрізняється їх використання? Коли я повинен використовувати відповідні класи та для яких цілей саме? у нас часто багато багатства, але його просто ми точно не знаємо, як відрізнити одне від іншого. Це один такий випадок для мене, що не в змозі знайти, чому я не повинен використовувати один з іншого.

  4. Я використовував клас секундоміра, щоб виміряти ці часи, які вважають найкращими. У сценарії мене завантажують тестуючи компонент, яким би був спосіб вимірювання часу відповіді. Секундомір, здається, найкраще рішення для мене. Будь-які думки вітаються.

ps: Є багато інструментів для тестування навантаження для веб-додатків. Шахта - це індивідуальний випадок компонентів сервера. І моє питання більше стосується створення N потоків за секунду.

Всі думки вітаються. Просто не думаю, що його так сильно не питання програмування. Звичайно, є. Це повинно задзвонити дзвіночкам для будь-якого програміста, який хоче самостійно перевіряти якість роботи, щоб знати продуктивність свого продукту, спочатку власноруч. Я перепробував багато варіантів, а потім повинен був відмовитися від того, як мені це зробити?


Файл говорить, чи стосується конкретної проблеми програмування і чи є її практичною відповідальною проблемою в професії програмування, її можна запитати. люди, які скептично ставляться до цього. будь ласка, прокоментуйте.
Король

Що ви маєте на увазі під «тією ж миттю»? Цікаво, чи можна будь-яким чином змусити TPL або PLinq досягти цього.
Герт Арнольд

Моє запитання - про генерування N запитів за секунду. Тож той самий момент у цьому сценарії мав на увазі моє розуміння використання паралелі, починаючи нитки паралельно.
Король

Ви робили якийсь послідовний аналіз?

3
Це може стосуватися програмування, але у вашій посаді є занадто багато питань (принаймні 4). Я б звів це до одного питання, яке ви хочете задати, перш ніж воно закриється, оскільки воно занадто широке. Надайте відповідну інформацію, як-от 10000, про яке ви тільки що згадали, кількість ядер у вашій тестовій машині). Показаний код зазвичай допомагає.
Герт Арнольд

Відповіді:


10

У мене немає всіх відповідей. Сподіваюся, я можу пролити трохи світла на це.

Для спрощення моїх попередніх тверджень про вбудовані моделі .NET, просто знайте, що бібліотека Parallel використовує завдання, а програму TaskScheduler за завданнями використовує ThreadPool. Чим вище ви йдете в ієрархії (ThreadPool знаходиться внизу), тим більше накладних витрат при створенні елементів. Цей додатковий наклад, звичайно, не означає, що він повільніше, але добре знати, що він є. Зрештою, ефективність вашого алгоритму в багатопотоковому середовищі зводиться до його дизайну. Те, що працює добре послідовно, може не працювати так само паралельно. Занадто багато факторів, щоб дати вам жорсткі та швидкі правила, вони змінюються залежно від того, що ви намагаєтесь зробити. Оскільки ви маєте справу з мережевими запитами, я спробую навести невеликий приклад.

Дозвольте констатувати, що я не знавець у розетках, і майже нічого не знаю про Zeroc-Ice. Я знаю про біт про асинхронні операції, і саме це вам справді допоможе. Якщо ви надсилаєте синхронний запит через сокет, під час дзвінка Socket.Receive()ваша нитка блокується до отримання запиту. Це не добре. Ваш потік більше не може надсилати запити, оскільки він заблокований. Використовуючи Socket.Beginxxxxxx (), запит вводу / виводу буде зроблений і поміщений у чергу IRP для сокета, і ваша нитка продовжуватиме працювати. Це означає, що ваш потік насправді може робити тисячі запитів у циклі, не блокуючи його взагалі!

Якщо я вас правильно зрозумів, ви використовуєте дзвінки через Zeroc-Ice у своєму тестовому коді, насправді не намагаєтесь досягти кінцевої точки http. Якщо це так, я можу визнати, що я не знаю, як працює Zeroc-Ice. Я б, однак, запропонувати після консультацій , перерахованих тут , в зокрема , частина: Consider Asynchronous Method Invocation (AMI). На сторінці показано це:

Використовуючи AMI, клієнт повертає нитку управління, як тільки надіслане виклик (або, якщо його неможливо відправити негайно, у черзі), що дозволяє клієнту використовувати цей потік для виконання іншої корисної роботи в середній час .

Що здається еквівалентом тому, що я описав вище, використовуючи розетки .NET. Можливо, існують інші способи покращити ефективність, коли намагаються зробити багато надсилань, але я б почав тут або з будь-якої іншої пропозиції, переліченої на цій сторінці. Ви дуже розпливчали дизайн своєї заявки, тому я можу бути більш конкретним, ніж я був вище. Пам'ятайте, не використовуйте більше потоків, ніж це абсолютно необхідно, щоб зробити те, що вам потрібно, інакше, швидше за все, ваша програма працює набагато повільніше, ніж вам потрібно.

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

var iterations = 100000;
for (int i = 0; i < iterations; i++)
{
    // The thread blocks here waiting for the response.
    // That slows down your loop and you're just wasting
    // CPU cycles that could instead be sending/receiving more objects
    MyObjectPrx obj = iceComm.stringToProxy("whateverissupposedtogohere");
    obj.DoStuff();
}

Кращий спосіб:

public interface MyObjectPrx : Ice.ObjectPrx
{
    Ice.AsyncResult GetObject(int obj, Ice.AsyncCallback cb, object cookie);
    // other functions
}

public static void Finished(Ice.AsyncResult result)
{
    MyObjectPrx obj = (MyObjectPrx)result.GetProxy();
    obj.DoStuff();
}

static void Main(string[] args)
{
    // threaded code...
    var iterations = 100000;
    for (int i = 0; i < iterations; i++)
    {
        int num = //whatever
        MyObjectPrx prx = //whatever
        Ice.AsyncCallback cb = new Ice.AsyncCallback(Finished);
        // This function immediately gets called, and the loop continues
        // it doesn't wait for a response, it just continually sends out socket
        // requests as fast as your CPU can handle them.  The response from the
        // server will be handled in the callback function when the request
        // completes.  Hopefully you can see how this is much faster when 
        // sending sockets.  If your server does not use an Async model 
        // like this, however, it's quite possible that your server won't 
        // be able to handle the requests
        prx.GetObject(num, cb, null);
    }
}

Майте на увазі, що більше тем! = Краща продуктивність при спробі надсилання сокетів (або дійсно що-небудь робити). Нитки не є магією, оскільки вони автоматично вирішать будь-яку проблему, над якою ви працюєте. В ідеалі вам потрібно 1 нитка на ядро, якщо нитка не витрачає багато часу на очікування, то ви можете виправдати наявність більше. Запуск кожного запиту у власному потоці - погана ідея, оскільки відбуватимуться переключення контексту та витрачання ресурсів. (Якщо ви хочете побачити все, що я написав про це, натисніть редагувати і подивіться на минулі редакції цієї публікації. Я видалив її, оскільки вона, здавалося, затуманила основну проблему.)

Ви точно можете зробити цей запит у нитках, якщо ви хочете робити велику кількість запитів за секунду. Однак не перестарайтеся створювати нитки. Знайдіть рівновагу і дотримуйтесь її. Ви отримаєте кращі показники, якщо будете використовувати асинхронну модель проти синхронної.

Я сподіваюся, що це допомагає.


Чому ти так багато говориш про продуктивність? Мабуть, це не те, чого хоче ОП.
svick

1
@svick добре, що у початкової публікації ops було 4 запитання, і вони задавали питання щодо виконання завдань паралельних vs, потім це було відредаговано, і тепер вони знову. Отже, багато з того, що ви прочитали, було результатом цього. Зрештою, хоч його питання має відношення до продуктивності, оскільки він має загальне уявлення правильне, але, очевидно, не вистачає його реалізації. Я вважаю, що мої загострені відповіді наприкінці відповідають на питання, яке він не редагував.
Крістофер Керренс

1
Я змушений був скоротити свої запитання, бо вони хотіли проголосувати за закриття. Тепер, здається, справді тут їх мати. @ChristopherCurrens +1 хороший бал для різниці з ниткою до завдань. Це розширило моє розуміння. Але я все ще застряг, як генерувати декілька N запитів за секунду? Який саме найкращий спосіб це зробити?
Король

@King - я думаю, я не був таким чітким, як я вважав, що є. Останні 3-4 абзаци, які я думав, допоможуть вам. Я припускав, що ти вже використовуєш цикл сортів. Якщо ви робили це, проблема полягає в тому, що ваш сокет, який надсилає / приймає, блокує і, таким чином, сповільнює ваші запити. Можливо, я знайду якийсь час, щоб розмістити якийсь приклад псевдо-коду.
Крістофер Керренс

Я не маю жодної проблеми в тому, щоб насправді надсилати їх через ICE. Проблема полягає в тому, що визначає реалізацію, яка фактично створила б N запитів і те, що можна сказати правдивим для цього числа, N.
Король

2

Я переходжу до питання 1) і перейду до №2, оскільки це, як правило, прийнятний спосіб досягти того, що ви шукаєте. Раніше для досягнення n повідомлень за секунду ви можете створити єдиний процес, який потім запустить p AppDomains. Кожен AppDomain в основному просто починає цикл запитів, коли буде досягнуто певного моменту часу (використовуючи Таймер). Цей час повинен бути однаковим для кожного AppDomain, щоб переконатися, що вони починають вражати ваш сервер в один і той же час.

Щось подібне повинно працювати для надсилання запитів:

WaitCallback del = state => 
{ 
    ManualResetEvent[] resetEvents = new ManualResetEvent[10000]; 
    WebClient[] clients = new WebClient[10000]; 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index] = new ManualResetEvent(false); 
        clients[index] = new WebClient(); 

        clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler (client_OpenReadCompleted); 

        clients[index].OpenReadAsync(new Uri(@"<REQUESTURL>"), resetEvents[index]); 
    } 

    bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000); 
    Complete(succeeded); 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index].Dispose(); 
        clients[index].Dispose(); 
    } 
}; 

while(running)
{
    ThreadPool.QueueUserWorkItem(del);
    Thread.Sleep(1000);
}

Це, ймовірно, призведе до зменшення продуктивності на тій машині, на якій ви працюєте, тому ви завжди можете реалізувати подібний цикл з декількох різних машин, якщо у вас є ресурси (використовуючи процеси замість домену додатків).

Щодо вашого третього запитання, дайте це посилання прочитати http://www.albahari.com/threading/

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


2
З якою можливою причиною вам доведеться створити тут окремі AppDomains? Це здається абсолютно непотрібним.
svick

0

Не турбуйтеся з нитками, якщо N досить невеликий. Щоб генерувати N запитів за секунду, використовуйте час настінного годинника ( DateTime.Now). Знайдіть час як до, так і після запиту, а потім додайте кнопку, Sleepщоб відкласти наступний запит.

Наприклад, з N = 5 (200 мс):

Before request: 12:33:05.014
After request: 12:33:05.077
Sleep(137)
Before request: 12:33:05.214
After request: 12:33:05.271
Sleep(131)

Це не ідеально; Ви можете виявити, що Sleepце не точно. Ви можете зберігати кількість відхилень (перед X'-й запитами час повинен бути X-1 / N пізніше) і відповідно відрегулювати період сну.

Як тільки N стає занадто великим, ви просто створите M потоків і дозволите кожному потоку генерувати N / M запити однаково.


Мені потрібно генерувати дуже велику кількість запитів. Тож це не може бути варіантом, оскільки воно випиє мою пам'ять (4 Гб оперативної пам’яті) навіть до 100 потоків.
Король

Я створив 20 000 запитів в секунду з одного потоку, в 250 К код. У вас недостатньо процесора для запуску 100 потоків (цей клас машин не має 4 Гб). Наступною проблемою було б витіснення всіх цих запитів; у вас є 10 Гбіт / с Ethernet між вашим творцем завантаження та сервером? Отже, ви можете перевірити свої фактичні вимоги.
MSalters

Для уточнення, у мене є щось на кшталт 20+ Gbps. Тож це не проблема. Про клас машин, на що ви хотіли б звернутися? кількість процесорів?
Король

@King: для просування 100 ниток я б очікував, що 48-ядерна машина. SGI продає машини з такою кількістю ядер, наприклад, але на тих, що зазвичай отримують 32 Гб або більше.
MSalters

0

Найпростіший спосіб досягти тестування навантаження для будь-якого проекту .NET - придбати Ultimate видання Visual Studio. Сюди входять вбудовані інструменти тестування, які допомагають скласти всі види випробувань, включаючи навантаження. Тести для завантаження можуть бути виконані за допомогою створення віртуальних користувачів на одному ПК або розподілених на декілька для більшої кількості користувачів, також існує невелика програма, яку можна встановити на цільових серверах для повернення додаткових даних протягом тривалості тесту.

Це дорого, однак, але остаточне видання має багато можливостей, тож якби все було використано, це було б більш розумною ціною.


0

Якщо ви просто хочете, щоб всі потоки X потрапляли на ваш ресурс точно в один і той же час, ви можете помістити кожен потік за засувкою зворотного відліку і вказати короткий період очікування між семафорними чеками.

C # має реалізацію (http://msdn.microsoft.com/en-us/library/system.threading.countdownevent(VS.100).aspx).

У той же час, якщо ви проводите стрес-тестування вашої системи, ви, можливо, хочете перевірити і умови перегонів, і в цьому випадку ви хочете встановити періоди сну потоку на кожній нитці, що коливається з часом з рандомізованою частотою та піками / перемичками.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.