Нагрузочное тестирование: как генерировать посекундные запросы?

14

У меня есть серверный компонент, который работает через Zeroc-ICE. Когда я захотел загрузить его, я подумал, что с помощью параллельной библиотеки для создания нескольких запросов это можно сделать. Но это не в конечном итоге так. Использовать библиотеку Parallel (Parallel.For) из C #, по-видимому, было проще, но, похоже, она не генерирует все параллельно в одно и то же мгновение. Так что это не может быть определение для создания N запросов в секунду. Как я должен это делать ? Я предполагаю, что любой, кто хочет сделать нагрузочное тестирование первым, действительно подумает об этом.

  1. Какой эффективный способ на самом деле создавать N запросов в секунду?

  2. Другой миф о параллельном программировании. Пожалуйста, сообщите нам, если вы использовали шаблоны параллельного программирования в C # или .Net в целом. Представь, у меня 5 процессов. Как запустят все пять процессов одновременно. Что это значит для моего потребления ресурсов? Я попытался прочитать многие материалы, доступные через Интернет, но я получаю все больше и больше вопросов, чем они, являясь ответом на мои вопросы.

  3. Я использовал Parallel.For и создал N потоков и измерил время. Затем я попробовал то же самое, используя Task.Factory.start для перечисления задач. Измеренное время было другим. Так в чем же разница между их использованием? Когда мне следует использовать соответствующие классы и для каких целей именно? У нас часто бывает много богатств, но мы просто не знаем, как отличить одно от другого. Это один из таких случаев для меня, я не могу понять, почему я не должен использовать одно из другого.

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

PS: Есть много инструментов нагрузочного тестирования для веб-приложений. Мой - индивидуальный случай серверных компонентов. И мой вопрос больше касается создания N потоков в секунду.

Все мнения приветствуются. Только не думайте, что это не вопрос программирования. Это конечно. Любому программисту, который хочет самому поучаствовать в QE, должно быть приятно слышать о производительности своего продукта, из первых рук. Я перепробовал много вариантов, а затем должен был остановиться на том, как я должен это делать?

король
источник
Часто задаваемые вопросы говорят, если это относится к конкретной проблеме программирования, и если это практическая ответственная проблема в профессии программиста, его можно задать. люди, которые скептически и отмечают это. пожалуйста, прокомментируйте.
король
Что вы имеете в виду под «тем же мгновением»? Интересно, можно ли каким-либо образом заставить TPL или PLinq добиться этого?
Герт Арнольд
Мой вопрос о генерации N запросов в секунду. То же самое мгновение в этом сценарии было направлено на то, чтобы я понял, что использование параллельного запускает параллельные потоки.
король
Вы сделали какой-нибудь последовательный анализ?
3
Это может относиться к программированию, но в вашем посте слишком много вопросов (по крайней мере, 4). Я бы свел его к одному вопросу, который вы хотите задать, прежде чем он будет закрыт, потому что он слишком широкий. Предоставьте соответствующую информацию, например, 10000, которые вы только что упомянули, количество ядер в вашей тестовой машине). Показ кода обычно помогает.
Герт Арнольд

Ответы:

10

У меня нет всех ответов. Надеюсь , я могу пролить некоторый свет на это.

Чтобы упростить мои предыдущие утверждения о моделях потоков .NET, просто знайте, что Parallel Library использует Tasks, а TaskScheduler по умолчанию для Tasks использует 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 вопроса, и они задавали вопросы о производительности параллельных и заданий, затем они были отредактированы, и теперь они вернулись. Итак, многое из того, что вы прочитали, было результатом этого. В конечном счете, хотя его вопрос имеет отношение к производительности, поскольку у него правильная общая идея, но ему явно не хватает реализации. Я считаю, что мои точечные ответы в конце отвечают на вопрос, который он не отредактировал.
Кристофер Роукс
1
Я был вынужден уменьшить свои вопросы, потому что они хотели проголосовать, чтобы закрыть. Теперь, кажется, это действительно здесь, чтобы иметь их. @ChristopherCurrens +1 хорошая точка для различий с пулом потоков к задачам. Это расширило мое понимание. Но я все еще застрял, как генерация N запросов в секунду действительно возможно? Какой именно лучший способ сделать это?
король
@ Кинг - я думаю, я был не так ясен, как думал. Последние 3-4 абзаца, которые я думал, помогут вам. Я предполагал, что вы уже используете какую-то петлю. Если вы это делаете, проблема заключается в том, что ваш сокет отправляет / получает блокирует, и, следовательно, замедляет ваши запросы. Может быть, я найду время опубликовать пример псевдокода.
Кристофер Уок
У меня нет проблем с отправкой их через ICE. Проблема в том, что определяет реализацию, которая фактически создала бы N запросов и что-то, что можно сказать верным для этого числа, N.
король
2

Я собираюсь пропустить вопрос 1) и перейти сразу к # 2, поскольку это, как правило, приемлемый способ выполнить то, что вы ищете. В прошлом для достижения n сообщений в секунду вы могли создать отдельный процесс, который затем будет запускать p доменов приложений. Каждый домен приложений в основном просто запускает цикл запроса, как только достигнут определенный момент времени (с использованием таймера). Это время должно быть одинаковым для каждого домена приложений, чтобы гарантировать, что они начинают попадать на ваш сервер в один и тот же момент времени.

Примерно так должно работать для отправки ваших запросов:

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
Какова возможная причина для создания здесь отдельных доменов приложений? Это кажется совершенно ненужным.
svick
0

Не беспокойтесь о нитях, если N достаточно мало. Чтобы сгенерировать N запросов в секунду, используйте время настенных часов ( DateTime.Now). Потратьте время как до, так и после запроса, затем добавьте a, 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 запросов одинаково.

MSalters
источник
Я должен генерировать очень большое количество запросов. Так что это не может быть вариантом, потому что он будет пить мою память (4 ГБ ОЗУ) даже до 100 потоков.
король
Я создал 20 000 запросов в секунду из одного потока в 250K кода. В любом случае у вас недостаточно процессоров для запуска 100 потоков (этот класс компьютеров не имеет 4 ГБ). Следующая проблема будет выталкивать все эти запросы; у вас есть 10 Гбит / с Ethernet между создателем нагрузки и вашим сервером? Итак, вы можете проверить свои реальные требования.
MSalters
Чтобы уточнить, у меня есть что-то вроде 20+ Гбит / с. Так что это не проблема. О классе машин, на что бы вы ссылались? количество процессоров?
король
@King: чтобы протолкнуть 100 потоков, я ожидал бы 48-ядерную машину Например, SGI продает машины с таким количеством ядер, но на тех, которые вы обычно получаете 32 ГБ или более.
MSalters
0

Самый простой способ выполнить нагрузочное тестирование для любого проекта .NET - это приобрести версию Visual Studio Ultimate. Это идет с интегрированными инструментами тестирования, чтобы помочь преформовать все виды тестов, включая нагрузочные тесты. Нагрузочные тесты могут быть предварительно сформированы путем создания виртуальных пользователей либо на одном ПК, либо распределенных по нескольким для большего числа пользователей, также есть небольшая программа, которая может быть установлена ​​на целевых серверах, чтобы возвращать дополнительные данные в течение теста.

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

Ryathal
источник
0

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

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

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

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

Кит Брингс
источник