2009-02-03 4 views
4

Вот моя примерная программа для веб-сервера сервера и на стороне клиента. Я столкнулся с проблемой производительности strnage, которая, даже если я увеличиваю количество потоков для вызова веб-сервисов, производительность не улучшается. В то же время потребление CPU/памяти/сети с панели производительности диспетчера задач невелико. Мне интересно, что такое узкое место и как его улучшить?многопоточная проблема с производительностью для вызова веб-службы

(Мой опыт испытаний, удвоит число потоков будет почти в два раза общее время отклика)

стороне клиента:

class Program 
{ 
    static Service1[] clients = null; 
    static Thread[] threads = null; 

    static void ThreadJob (object index) 
    { 
     // query 1000 times 
     for (int i = 0; i < 100; i++) 
     { 
      clients[(int)index].HelloWorld(); 
     } 
    } 

    static void Main(string[] args) 
    { 
     Console.WriteLine("Specify number of threads: "); 
     int number = Int32.Parse(Console.ReadLine()); 

     clients = new Service1[number]; 
     threads = new Thread[number]; 

     for (int i = 0; i < number; i++) 
     { 
      clients [i] = new Service1(); 
      ParameterizedThreadStart starter = new ParameterizedThreadStart(ThreadJob); 
      threads[i] = new Thread(starter); 
     } 

     DateTime begin = DateTime.Now; 

     for (int i = 0; i < number; i++) 
     { 
      threads[i].Start(i); 
     } 

     for (int i = 0; i < number; i++) 
     { 
      threads[i].Join(); 
     } 

     Console.WriteLine("Total elapsed time (s): " + (DateTime.Now - begin).TotalSeconds); 

     return; 
    } 
} 

стороне сервера:

[WebMethod] 
    public double HelloWorld() 
    { 
     return new Random().NextDouble(); 
    } 

заранее спасибо, George

+0

Обновите исходный вопрос, а не отправляйте ответы на свой вопрос. –

ответ

5

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

Итак, проверьте свои память и счетчики процессов в perfmon, если вы посмотрите на переключатели контекста и его высокий (более 4000 в секунду), тогда у вас проблемы.

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

Наконец, проверьте диск ввода-вывода, по той же причине, что и выше.

Резолюция заключается в том, чтобы удалить блокировки или удерживать их в течение как минимум времени. Наша проблема была решена путем устранения зависимости от COM BSTR и их глобальной блокировки, вы обнаружите, что C# имеет множество подобных узких мест синхронизации (предназначенных для безопасного функционирования вашего кода). Я видел падение производительности, когда я переместил простое приложение C# из одноядерного в многоядерный.

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

0

Ну, в этом случае вы не балансируете свою работу между выбранными n.º нитями ... Каждая созданная вами нить будет выполнять тот же Job. Поэтому, если вы создаете n потоков, и у вас ограниченная емкость параллельной обработки, производительность, естественно, уменьшается. Другое мнение, что я заметил, что требуемый Job является относительно быстрой операцией для 100 итераций, и даже если вы планируете делить это задание на несколько потоков, вам нужно учитывать, что время, затрачиваемое на переключение контекста, создание/удаление потоков будет важным фактором в общем времени.

7

Хотя вы создаете многопоточный клиент, имейте в виду, что .NET имеет настраиваемое узкое место из двух одновременных вызовов на один хост. Это по дизайну. Обратите внимание, что это на клиенте, а не на сервере.

Попробуйте изменить файл app.config в клиенте:

<system.net> 
<connectionManagement> 
    <add address=“*” maxconnection=“20″ /> 
</connectionManagement></system.net> 

Существует еще некоторая информация об этом в this short article:

0

Как уже упоминалось Брюно, ваш веб-метод является очень быстрой операцией. В качестве эксперимента попробуйте убедиться, что ваш метод HelloWorld занимает немного больше времени. Бросьте в Thread.Sleep (1000), прежде чем вернуть случайный двойной.Это сделает более вероятным, что ваша служба фактически вынуждена обрабатывать запросы параллельно. Затем попробуйте своего клиента с различным количеством потоков и посмотрите, как отличается производительность.

3

Я не считаю, что вы на самом деле сталкиваетесь с узким местом.

Вы попробовали то, что я предложил?

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

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

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

1

Конечно, добавление сна не улучшит производительность.

Но точка испытания должна тестироваться с переменным количеством потоков. Итак, держите Сон в своем WebMethod.

И попробуйте использовать только 5, 10, 20 потоков.

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

Вы понимаете, что в своем тесте, когда вы удваиваете количество потоков, вы удваиваете объем выполняемой работы. Поэтому, если ваши потоки не выполняются параллельно, тогда вы, конечно, увидите линейное увеличение общего времени ...

Я проверил простой тест, используя ваш код клиента (со сном на службе). Для 5 потоков я видел общее время около 53 секунд. И за 10 потоков, 62 секунд. Итак, для 2x количества вызовов в веб-сервисе это заняло всего 17% больше времени. Это то, чего вы ожидаете, нет?

0

Попытайтесь использовать какую-то процессорную задачу вместо Thread.Sleep. Фактически комбинированный подход является лучшим.

Сон просто передает временную рамку потока в другую нить.

2

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

return new Random().NextDouble(); 

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

Джейсон
0

IIS AppPool "Maximum рабочих процессов" устанавливается в 1 по умолчанию. По какой-то причине каждый рабочий процесс ограничивается 10 вызовами службы за раз. Моя функция асинхронного сервера WCF отключается (10 * 1000); только. Это то, что происходит, когда максимальная рабочие процессы = 1 http://s4.postimg.org/4qc26cc65/image.png

альтернативно

http://i.imgur.com/C5FPbpQ.png?1

(первый пост на SO, мне нужно, чтобы объединить все фотографии в одну картину.)

Клиент делает 48 асинхронных вызовов WCF WS в этом тесте (используя 16 процессов). В идеале это займет около 10 секунд (Sleep (10000)), но требуется 52 секунды. Вы можете увидеть 5 горизонтальных линий в изображении perfmon (выше ссылки) (используя perfmon для мониторинга текущих соединений веб-служб на сервере). Каждая горизонтальная линия длится 10 секунд (что делает сон (10000)). Существует 5 горизонтальных линий, так как сервер обрабатывает 10 вызовов каждый раз, а затем закрывает 10 подключений (это происходит 5 раз для обработки 48 вызовов). Завершение всех вызовов заняло 52 секунды.

После установки Максимальная рабочих процессов = 2 (в той же картине, приведенной выше) на этот раз есть 3 горизонтальные линии, так как сервер обрабатывает 20 вызовов каждый раз, затем закрывает, что 20 соединений (это происходит в 3 раза, чтобы обработать 48 вызовов). Взял 33 секунды.

После установки Максимальных рабочих процессов = 3 (на том же изображении, приведенном выше) На этот раз есть 2 горизонтальные линии, так как сервер обрабатывает 30 вызовов каждый раз. (происходит 2 раза для обработки 48 звонков) Взял 24 секунды.

После установки максимальных рабочих процессов = 8 (на том же изображении, приведенном выше) На этот раз имеется 1 горизонтальная линия, так как сервер обрабатывает 80 вызовов каждый раз. (происходит один раз для обработки 48 вызовов) Взял 14 секунд.

Если вам не нравится эта ситуация, ваши параллельные (асинхронные или поточные) клиентские вызовы будут поставлены в очередь на 10 секунд на сервере, тогда все ваши поточные вызовы (> 10) не будут обработаны сервером в параллельны друг другу.

PS: Я использовал Windows 8 x64 с IIS 8.5. Ограничение 10 одновременных запросов предназначено для операционных систем Windows. Серверные ОС не имеют этого ограничения в соответствии с другим сообщением на SO (я не могу дать ссылку из-за rep < 10).